I can't get a checked ListBox to work.
My business object (it's a private/nested class, hence the lower-case)
class shop : System.ComponentModel.INotifyPropertyChanged
{
internal int id;
string _name;
internal string name
{
get { return _name; }
set
{
_name = value;
if (PropertyChanged != null) PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("name"));
}
}
bool _selected;
internal bool selected
{
get { return _selected; }
set
{
_selected = value;
if (PropertyChanged != null) PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("selected"));
}
}
}
My XAML:
<ListBox ItemsSource="{Binding}" Grid.Row="1" HorizontalAlignment="Stretch" Margin="10,0,10,0" Name="lbSelectedShops" VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Width="Auto" Content="{Binding Path=name}" IsChecked="{Binding Path=selected, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Databinding in code behind is very simple:
lbSelectedShops.ItemsSource = _shops;
where _shops is an ObservableCollection<shop> (containing two elements).
What I get is two blank checkboxes in the listbox (no captions, and both ticked off, even though selected is set to true for all items in the ItemsSource).
I'm really frustrated already and I'm sure it must be something very trivial. What is wrong here?
It's not working because your properties are internal and for Databinding you need public properties.
From MSDN (Binding Sources Overview):
You can bind to public properties, sub-properties, as well as
indexers, of any common language runtime (CLR) object. The binding
engine uses CLR reflection to get the values of the properties.
Alternatively, objects that implement ICustomTypeDescriptor or have a
registered TypeDescriptionProvider also work with the binding engine.
Binding work only with public properties (and public classes)
Related
I am trying to make a program that gives you a CRUD interface for List of any objects you give it. That includes:
Showing all of their properties inside a ListBox
The ability to insert a new object
The ability to update an object
The ability to delete an object
Keep in mind that, at the compile-time, I have no idea what kind of objects I am getting. For example, I want to have a TextBlock for each of the properties simply listed inside ListBox's DataTemplate. So how would I do the data binding if I don't know the name of the property? Also, how would I generate an insertion form when I don't know property names?
And finally, is it possible to do it using pure MVVM Pattern, without any Code-Behind?
Thanks
One option: Wrap PropertyInfo in a PropertyInfoViewModel so you can bind to it's value:
class PropertyInfoViewModel
{
Object CRUDObject { get; set; }
PropertyInfo PropertyInfo { get; set; }
Object Value {
get
{
return PropertyInfo.GetValue(CRUDObject);
}
set
{
PropertyInfo.SetValue(CRUDObject, value);
}
}
}
You could have an ObservableCollection in your CRUDObjectViewModel, populated when you create it or change the CRUD it's attached to (Look up reflection if confused by this).
Use a template selector to choose a particular editor to display for the PropertyInfoViewModel:
public class PropertyTypeTemplateSelector : DataTemplateSelector
{
public DataTemplate BooleanTemplate { get; set; }
public DataTemplate GuidTemplate { get; set; }
public DataTemplate StringTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
PropertyInfo propertyInfo = (item as PropertyInfoViewModel).PropertyInfo;
if (propertyInfo.PropertyType == typeof(Boolean))
{
return BooleanTemplate;
}
else if (propertyInfo.PropertyType == typeof(Guid))
{
return GuidTemplate;
}
else if (propertyInfo.PropertyType == typeof(String))
{
return StringTemplate;
}
return null;
}
}
You could use it like this:
<ListBox ItemsSource="{Binding Properties}">
<ListBox.Resources>
<DataTemplate x:Key="BooleanTemplate">
<CheckBox Content="{Binding PropertyInfo.Name}" IsChecked="{Binding Value}"/>
</DataTemplate>
<DataTemplate x:Key="GuidTemplate">
<StackPanel>
<TextBox Text="{Binding PropertyInfo.Name}"/>
<TextBox Text="{Binding Value, ValueConverter={StaticResources MyGuidConverter}}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="StringTemplate">
<StackPanel>
<TextBox Text="{Binding PropertyInfo.Name}"/>
<TextBox Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Null"/>
</ListBox.Resources>
<ListBox.ItemTemplateSelector>
<helpers:PropertyTypeTemplateSelector BooleanTemplate="{StaticResource BooleanTemplate}"
GuidTemplate="{StaticResource GuidTemplate}"
StringTemplate="{StaticResource StringTemplate}"/>
</ListBox.ItemTemplateSelector>
</ListBox>
Might have to think about how to deal with changes/updates though, as this isn't using NotifyPropertyChanged to keep the UI up to date.
I've not tested any of this, but it should work, I think.
This control WPFCrudControl may fit with your problem.
A generic WPF CrudControl implemented based on the MVVM pattern. It gives a huge productivity boost for straightforward CRUD operations (Add, Edit, Delete, Validate, Listing with sorting, paging and searching). The control abstracts both the UI and business logic, so it requires relatively minimal coding effort, while keeping it possible to customize its behavior.
I am using a DataTemplateSelector inside a ContentControl. I have 3 different DataTemplates based on 3 different object types. When I set the content of my ContentControl to data of the mentioned types, the DataTemplateSelector swaps to the specific DataTemplate AND the selector futhermore seems to rollback/reset the values from the old template. Why is that so?
Edit: I figured out that the values get resetted because I have an attached property caled Prop and inside its OnPropertyChangedCallback it notifies me about the Prop having value null on swapping between DataTemplates. You can see that attached property in code below.
Can somebody help me out what happens behind this swapping mechanism of DataTemplateSelector?
Here is a deeper explaination with code:
public void Window1()
{
InitalizeComponents();
}
public void OnClick(object sender, RoutedEventArgs e)
{
if(this.DataContext == null)
this.DataContext = "Hallo";
else{
if(this.DataContext is string)
this.DataContext = 123;
else{
if(this.DataContext is int)
this.DataContext = null;
}
}
}
By clicking on Button few times I change the type and so in ContentControl the selector changes to DataTemplate.
The selector looks like this below. It swaps between textDataTemplate and numericDataTemplate and one more template. As I mentioned i have those three type which are string, int, and one more, that i wish not to metion. Their DataTemplates are called textDataTemplate, numericDataTemplate and that one more. :)
<local:MyTemplateSelector x:Key="dataTemplateSelector"
TextTemplate="{StaticResource textDataTemplate}"
NumericTemplate="{StaticResource numericDataTemplate}"/>
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate TextTemplate;
public DataTemplate NumericTemplate;
public DataTemplate Select(object item, Culture.....)
{
if(item is string)
{
return this.TextTemplate;
}
else
{
return this.NumericTemplate;
}
}
}
ContentControl and XAML looks like this:
<Button Click="OnClick" Content="Click Me"/>
<ContentControl Name="contentCtrl"
Content="{Binding}"
Width="123"
ContentTemplateSelector="{StaticResource dataTemplateSelector}" />
And this is how textDataTemplate looks alike.
<DataTemplate x:Key="textDataTemplate">
<TextBox x:Name="text" my:AttProperties.Prop="{extension:MarkupExt value}" Text="{Binding Path=Txt, Mode=Default, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>
numericDataTemplate looks similar to textDataTemplate just that only digits are allowed.
The Prop is my attached property from AttProperties class of type string. The Prop is somewhere inside of all three DataTemplate. Above the Prop is sitting on a TextBox but it could be a Label too. The markupextension is just a "return Hello". The extension is just there to test how to create a custom markupextension. There is no big deal with the extension. It shouldnt have to do much with the swapping of DataTemplates.
One more time to explain my problem. Swapping seems reselts/rollback my old templates. I swap from textDataTemplate to lets say numericDataTemplate and the Prop of textDataTemplate gets set to null but the value before was "Hello".
Why is that happening? It seems like the same behavior with using tiggers. Once a Trigger is no more valid it rollsback the used values. Is a DataTemplateSelector using some kind of same mechanism as Triggers?
Edited:
The attached property is just a simple .RegisterAttached with an OnPropertyChangedCallback. Inside OnPropertyChangedCallback I figured the prop is null when swapping the dataTemplates.
If you use two-way binding in numeric template and it only accepts something like Double, it can set value to number. But no one can be sure without seeing full code. It's possible that your own code does something wrong.
To understand things better, create your own control, derived from the ContentControl, and use it in your sample. Then override control methods OnContentxxxChanged, insert breakpoints there and debug your application. You should understand, what's going on with your data and with template selector. When application stops on breakpoint, carefully check all values and look at stack trace. To debug bindings you can insert IValueConverters, it would give you place in code, where you can check values.
I really suggest you to make the simplest working thing first, and then go to more complicated things such as textboxes with two-way bindings to some property of some control which you didn't show in your question. Here is a working version with TextBlocks:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void OnClick(object sender, RoutedEventArgs e)
{
if (this.DataContext == null)
this.DataContext = "Hallo";
else if (this.DataContext is string)
this.DataContext = 123;
else if (this.DataContext is int)
this.DataContext = null;
}
}
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate TextTemplate {get; set;}
public DataTemplate NumericTemplate {get; set;}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is string)
{
return this.TextTemplate;
}
else
{
return this.NumericTemplate;
}
}
}
and xaml:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="numericDataTemplate">
<TextBlock Foreground="Red" Text="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="textDataTemplate">
<TextBlock Foreground="Green" Text="{Binding}"/>
</DataTemplate>
<local:MyTemplateSelector x:Key="dataTemplateSelector"
TextTemplate="{StaticResource textDataTemplate}"
NumericTemplate="{StaticResource numericDataTemplate}"/>
</Grid.Resources>
<StackPanel>
<Button Click="OnClick" Content="Click Me" VerticalAlignment="Top"/>
<ContentControl Name="contentCtrl"
Content="{Binding}"
Width="300" Height="100"
ContentTemplateSelector="{StaticResource dataTemplateSelector}" />
</StackPanel>
</Grid>
Compare with your code. When you inherit from DataTemplateSelector, you should override SelectTemplate method and don't invent methods with other names. All controls such as ContentControl will only use SelectTemplate. Etc..
Obviously, all works and DataTemplateSelector does nothing wrong. I suppose, your problem is somewhere in your data and bindings
And look at your OnClick method - it always sets DataContext to null
I've got a WPF application using Caliburn.Micro. I want to be able to overlay the application with a shadow and progress ring (from MahApps.Metro) when I want the application to wait for some work to be done in the background.
What I have at the moment actually works but the overlay is always-on at design time. My ShellView window looks like this:
<Window ...>
...
<Grid>
...
<Rectangle x:Name="waitShadow" Fill="#3f000000" Stroke="Black" StrokeThickness="0" Visibility="{Binding IsWaiting, Converter={StaticResource BooleanToVisibilityConverter}}" Grid.RowSpan="2"/>
<ContentControl ... Visibility="{Binding IsWaiting, Converter={StaticResource BooleanToVisibilityConverter}}">
<Controls:ProgressRing ...> <!-- from MahApps.Metro -->
</Controls:ProgressRing>
</ContentControl>
</Grid>
</Window>
My ShellViewModel class has a public bool property IsWaiting and when I set it to true the shadow and ring comes up and everything is disabled. When I set it to false it goes back to normal, so the binding works (I'm using Fody with the PropertyChanged addin). The only problem is that the Visibility property isn't collapsed at design time.
Is there a better way to have an overlay that works at design time?
You can set a FallbackValue on your binding, that will Collapse it in design time
Visibility="{Binding IsWaiting, Converter={StaticResource BooleanToVisibilityConverter}, FallbackValue=Collapsed}"
You could also make IsWaiting a DependancyProperty and set the default there, But I find this the easiest solution.
FallbackValue doesn't always work, i.e. if your designer is actually bound to design time data, and FallbackValue actually modifies run-time behaviour of the binding which may be less than desirable in many situations. I made a markup extension that lets designers fiddle with the UI in the designer without worrying about messing up run-time behaviour. I wrote about it here: http://www.singulink.com/CodeIndex/post/wpf-visibility-binding-with-design-time-control
It can be used like this:
<Grid Visibility="{data:Value {Binding RootObject, Converter={StaticResource NullToVisibilityConverter}}, DesignValue=Visible}">
<TextBlock Background="Red" Text="Testing visibility" />
</Grid>
The code for ValueExtension is as follows (any updates or bug fixes will be posted to the blog so I suggest checking there for the latest version):
public class ValueExtension : MarkupExtension
{
public object DesignValue { get; set; } = DependencyProperty.UnsetValue;
[ConstructorArgument("value")]
public object Value { get; set; } = DependencyProperty.UnsetValue;
public ValueExtension() { }
public ValueExtension(object value)
{
Value = value;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var property = provideValueTarget.TargetProperty as DependencyProperty;
var target = provideValueTarget.TargetObject as DependencyObject;
if (target == null || property == null)
return this;
object value = DesignerProperties.GetIsInDesignMode(target) && DesignValue != DependencyProperty.UnsetValue ? DesignValue : Value;
if (value == DependencyProperty.UnsetValue || value == null)
return value;
if (value is MarkupExtension)
return ((MarkupExtension)value).ProvideValue(serviceProvider);
if (property.PropertyType.IsInstanceOfType(value))
return value;
return TypeDescriptor.GetConverter(property.PropertyType).ConvertFrom(value);
}
}
I have a view model to manage a dialog type of view that allows filtering of a listing (if necessary) and selection of an item. The code works fine whether I set IsSynchronizedWithCurrentItem to true or not. My understanding is that this property is not true by default in a ListView, so I'd like to better understand this property.
Here is the binding setup in the view's xaml (which works just as well without the synch property setting):
<ListView
ItemsSource="{Binding Projects.View}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedProject, Mode=TwoWay}"
>
The view model Projects is actually a CollectionViewSource that is backed by a private ObservableCollection. I think I glommed this idea from a sample project of Josh Smith's, but I honestly don't recall right now. Here is the relevant portion of the VM that relates to the xaml binding:
private ObservableCollection<ProjectViewModel> _projectsInternal { get; set; }
public CollectionViewSource Projects { get; set; }
private void _populateProjectListings(IEnumerable<Project> openProjects) {
var listing = (from p in openProjects
orderby p.Code.ToString()
select new ProjectViewModel(p)).ToList();
foreach (var pvm in listing)
pvm.PropertyChanged += _onProjectViewModelPropertyChanged;
_projectsInternal = new ObservableCollection<ProjectViewModel>(listing);
Projects = new CollectionViewSource {Source = _projectsInternal};
}
/// <summary>Property that is updated via the binding to the view</summary>
public ProjectViewModel SelectedProject { get; set; }
The Filter property of the CollectionViewSource has a handler which returns various predicates on the view model items in the list which is picked up by the bindings correctly. Here is the gist of that code (which is in the same ProjectSelctionViewModel):
/// <summary>Trigger filtering of the <see cref="Projects"/> listing.</summary>
public void Filter(bool applyFilter)
{
if (applyFilter)
Projects.Filter += _onFilter;
else
Projects.Filter -= _onFilter;
OnPropertyChanged<ProjectSelectionViewModel>(vm=>vm.Status);
}
private void _onFilter(object sender, FilterEventArgs e)
{
var project = e.Item as ProjectViewModel;
if (project == null) return;
if (!project.IsMatch_Description(DescriptionText)) e.Accepted = false;
if (!project.IsMatch_SequenceNumber(SequenceNumberText)) e.Accepted = false;
if (!project.IsMatch_Prefix(PrefixText)) e.Accepted = false;
if (!project.IsMatch_Midfix(MidfixText)) e.Accepted = false;
if (!project.IsAvailable) e.Accepted = false;
}
Setting the Mode=TwoWay is redundant since the ListView's SelectedItem binding is two way by default, but I don't mind being explicit about it (I might feel differently about that once I understand WPF better).
What about my code is making the IsSynchronizedWithCurrentItem=True redundant?
My gut is that this is decent code, but I don't like that pieces of it seem to be working via "magic", which means I would welcome any constructive feedback!
Cheers,
Berryl
IsSynchronizedWithCurrentItem syncs the CurrentItem of the default CollectionView of the bound collection with the SelectedItem of your control.
Since you never use the CurrentItem of the CollectionView and you do not appear to bind to the same collection twice, setting the property in question has no visible effect at all.
Demo of how the property syncs (for XAML viewers like XAMLPad):
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<x:Array x:Key="Items" Type="{x:Type sys:String}">
<sys:String>Apple</sys:String>
<sys:String>Orange</sys:String>
<sys:String>Pear</sys:String>
<sys:String>Lime</sys:String>
</x:Array>
</Page.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Background="Transparent">
<ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{StaticResource Items}" />
<ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{StaticResource Items}" />
<!-- This TextBlock binds to the CurrentItem of the Items via the "/" -->
<TextBlock Text="{Binding Source={StaticResource Items}, Path=/}"/>
</StackPanel>
</ScrollViewer>
</Page>
I'm trying to bind two ListBoxes:
<ListBox SelectionChanged="lbApplications_SelectionChanged"
ItemsSource="{Binding Path=Applications,
UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
<ListBox DisplayMemberPath="Message"
ItemsSource="{Binding Path=Events,
UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
Applications and Events are public properties in Window class.
I set DataContext to this to both list boxes and implement INotifyPropertyChanged in Window class:
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
And then after adding new item to Applications or Events I call:
NotifyPropertyChanged("Events");
NotifyPropertyChanged("Applications");
The issue is that ListBox is loaded only one time. What am I doing wrong?
Let's just look at one of the ListBoxes, since they're both the same, basically.
The code we're concerned about is this:
<ListBox ItemsSource="{Binding Path=Applications,
UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
Since you're new to WPF, let me say you probably don't need UpdateSourceTrigger or Mode in there, which leaves us with this:
<ListBox ItemsSource="{Binding Path=Applications}" />
You mentioned that Applications is a public property in your code-behind. You need it to be a DependencyProperty, and you need it to fire events when it changes -- most people use an ObservableCollection for this.
So your code-behind will have something like this:
public ObservableCollection<string> Applications
{
get { return (ObservableCollection<string>)GetValue(ApplicationsProperty); }
set { SetValue(ApplicationsProperty, value); }
}
public static readonly DependencyProperty ApplicationsProperty =
DependencyProperty.Register("Applications",
typeof(ObservableCollection<string>), typeof(Window1),
new UIPropertyMetadata(null));
Then, where you want to add it, you'll do something like this:
this.Applications = new ObservableCollection<string>();
Applications.Add("Whatever");
Finally, for the "simple" binding syntax to work in the XAML, I usually change the DataContext in my Window (or the root Control element for the file, whatever I'm working in) to
<Window DataContext="{Binding RelativeSource={RelativeSource Self}}" ... >
...
Your Applications box will update automatically.
The problem is that your property value hasn't changed. It's still the same list, same reference.
One solution might be that your collections are of type ObservableCollection. These lists provide events for WPF when you add or remove items.