I have a listview that's item source is databound to an observable collection of a custom datatype. Each object in the list has the property DisplayName (string) and IsChecked (bool), among others. The data template essentially is :
<CheckBox VerticalAlignment="Center"
Content="{Binding DisplayName}"
IsChecked="{Binding IsChecked}"/>
When I check the boxes, I am able to determine, easily, the underlying items that now have been checked. However in the viewmodel, if i wish to uncheck an item, it does not seem to work going the other way.
I have NotifyProperty change implemented in the setter for the observable collection, but do i need to do something special for the bool?
In the Viewmodel, the following code is where I attempt to uncheck the checked checkboxes (as kind of a refresh after a command has run):
foreach (var bill in AllBills)
{
bill.IsChecked = false;
}
I have NotifyProperty change implemented in the setter for the observable collection, but do i need to do something special for the bool?
Yes. When you give your collection property a new collection, you raise PropertyChanged for that property. What does that have to do with property values changing on an item in the collection? Nothing at all. When you set IsChecked on one of those items, is there any imaginable way for that code to hit the setter for your collection property? Nope.
Your items in the collection must themselves implement INotifyPropertyChanged, and the setter for IsChecked must raise PropertyChanged when the value of IsChecked changes. If you did that for the collection property, I expect you already know how. But shoot me a comment if you run into any snags.
One of the tough parts of the WPF learning curve is developing a reliable intuitive sense of the borderline between things that just happen by secret framework rainbow pony magic, and things you have to do yourself. One spot on that borderline is where you're standing right now.
If you implement INotifyPropertyChanged in your viewmodel, and are firing the event in IsChecked property, then just change your checkbox to:
<CheckBox VerticalAlignment="Center"
Content="{Binding DisplayName}"
IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
Related
I have this situation:
A IsToolbarButtonsEnabledProperty DependencyProperties
A have plenty of other DependencyProperties in a class (a huge class, needs to be this way)
A serie of Buttons on a toolbar.
The (IsEnabled) property of each of these buttons is a function of (IsToolbarButtonsEnabledProperty) throught a special converter, the buttons a differenced by ConvertParameter ("PreviousButton", "NextButton"...)
Opacity="{Binding IsEnabled, RelativeSource={RelativeSource Self}, Converter={StaticResource OpacityBoolToIntConverter}, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=DPEnableLinks, Converter={StaticResource ToolButtonEnableConverter}, ConverterParameter='ZoomOut' }"
ToolButtonEnableConverter is a converter that compares ConverterParameter "PreviousButton" with other value of other dependency property (in class). I have to many DP to make one multivalueconverter, so I read them straight from my class ((MainWindow)App.Current.MainWindow;)
Questions
When I update other DPs the value isEnabled / Opacity, dont change. How to fix this?
Is there a general solution to make a Binding refresh everytime a DP changes.
(Repeating myself): I will be adding more and more DPs over time, so a MultiValueConverter seams odd.
One way to force the Binding to update is to create a (meaningless) property and add it to the Binding (using MultiBinding), and when you want to update your Binding you change that property, and all the Binding is updated.
I must add that the more "straightforward" way is to use MultiBinding to all the relevant properties. If you have way to many properties that you need to bind, maybe you should re-think if you can build this functionality some other way.
Got a tough one. Consider a ViewModel that is comprised of a list of objects, where each object defines an int value, and some of those objects also define a Presets dictionary of ints keyed on a 'friendly' string representing that value in the UI.
Here's an example...
List<SomeItem> AllItems;
public class SomeItem : INotifyPropertyChanged
{
public SomeItem(int initialValue, Dictionary<int,string> presets)
{
this.CurrentValue = initialValue;
this.Presets = presets;
}
public int CurrentValue{ get; set; } // Shortened for readability. Assume this property supports INPC
public Dictionary<int,string> Presets{ get; private set; }
}
The goal for the UI is if the item has no presets, the user can enter any int value they want. However, if there are presets, we want to limit them to those values and also display them in the UI as the Friendly names from the dictionary.
Our first attempt was to use a TextBox and a ComboBox, modifying their visibilities depending on if there were presets or not, like this...
<ComboBox ItemsSource="{Binding Presets}"
DisplayMemberPath="Key"
SelectedValuePath="Value"
SelectedValue="{Binding CurrentValue, Mode=TwoWay}"
Visibility={Binding HasPresets, Converter=...}">
<TextBox Text="{Binding CurrentValue}"
Visibility={Binding HasPresets, Converter...}" /> // Assume the inverse of the combo
...but when we're using this in a DataTemplate of a list that supports virtualization, the combo occasionally displays blanks. I believe is because when the item is reused and the DataContext changes, SelectedValue updates before ItemsSource meaning there's potentially no preset value to match on, thus the proposed SelectedValue value gets tossed by the control, then ItemsSource updates, but there's no selected value so it shows a blank.
My next thought (and what we preferred anyway) was to use only a TextBox that displayed the preset name but was actually bound to Value, then use a converter to work its magic, and let the user type either the friendly name or the actual value. If the user typed something that wasn't a valid value or a preset, we'd just throw an error. If there were no presets, it would simply act as a pass-through of the value.
However, there I'm not sure how to pass in the presets to the converter. You can't set a binding on a ConverterParameter to pass them in that way, and if you use a multi-binding, then I'm not sure how to structure the 'ConvertBack' call since there too I need them passed in, not sent back out.
I'm thinking the proper way is to implement UiValue in the ViewModel which we'd simply bind to like this...
<TextBox Text="{Binding UiValue}" />
...then move the code that would've been in the converter to that property's getter/setter implementation, or simply be a pass-through to Value if there are no presets. However, this seems like too much logic is going in the ViewModel where it should be in the View (ala a converter or similar.) Then again, maybe that's exactly the point of the ViewModel. I dunno. Thoughts welcome.
Personally, I would go for putting the 'converter code' into the property as you suggested... I don't see any problem with having the code in there. In fact, it's probably better than having it in a Converter because then you can easily test it too.
Sorry, this isn't much of an answer, but I felt that your question deserved at least one.
I like your question, because it illustrates the way of thinking that stands behind the existence of a ViewModel in WPF. Sometimes they seem inevitable.
Converters are designed to be stateless, which is why it's difficult to pass context variables like presets. ViewModel is a layer, of which responsibility is to prepare Model for binding purposes. The role of a "model" is to handle logic. Thus, a ViewModel may handle in detail the behaviour (logic) of a View. This is precisely what you want. Most of the time I find myself not needing Converters at all.
Sometimes it feels more natural that the view logic should be in the View, but then ViewModel seems superfluous. However, when that logic is located in the ViewModel it's usually easier to auto-test. I wouldn't be afraid of putting stuff like this in ViewModel at all. Often this is the easiest (and correct) way.
Have UiValue property in ViewModel and handle conversion there:
public string UiValue{ get{/*...*/} set{/*...*/} }
To rephrase, in WPF there is no clean way to replace the property you bind to. E.g. if you wanted to have
<TextBox Text="{Binding IntValue}" />
change at some point to:
<TextBox Text="{Binding PresetValue}" />
you're trapped. This is not how things are done. Better have a constant binding like
<TextBox Text="{Binding UiValue}" />
and deal with the logic behind the UiValue property.
Another possible approach (instead of playing with visibility of ComboBox and TextBox) is to have a DataTemplateSelector, which would decide whether a ComboBox or TextBox should be created for SomeItem. If presets are null or empty select TextBox-based DataTemplate, otherwise take ComboBox. If I'm not mistaken you'd have to investigate FrameworkElement.DataContext property from within the selector to find the context (presets).
Considering your doubt about ConvertBack method, most commonly value or Binding.DoNothing is returned in case you don't need conversion in any of the directions.
I have a ComboBox in WPF binding its ItemsSource Property to a Property returning an IEnumerable of String. The binding is just one-way. The class that contains the data for the ComboBox implements INotifyPropertyChanged Interface and calls the OnPropertyChanged(..) as soon as the Property gets updated. When I leave the ComboBox untouched the changes are correctly propagated. But as soon as the ComboBox is expanded once or a value is selected the changes in the ItemsSource Collection are no longer updated. What may be the reason for this behaviour?
Heres the XAML
<ComboBox Name="cmbSourceNames"
Grid.Row="0"
SelectedItem="{Binding Path=CurrentSource, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=SourceAddresses, NotifyOnSourceUpdated=True}"/>
The DataContext is set in the code behind:
this.cmbSourceNames.DataContext = this._dpa;
And this one is the Method that triggers the change of the Property. The Method for adding the Packet is delegated to the Current Dispatcher with BeginInvoke.
private void DispatcherAddDataPacket(DataPacket dp)
{
ObservableCollection<DataPacket> dpList;
this._dpkts.TryGetValue(dp.SourceAddress, out dpList);
if (dpList == null)
{
dpList = new ObservableCollection<DataPacket>();
dpList.Add(dp);
this._dpkts.Add(dp.SourceAddress, dpList);
OnPropertyChanged("SourceAddresses");
}
else
{
dpList.Add(dp);
}
}
The Property is giving back the Keys of the Dictionary as IEnumerable.
Finally I implemented the Binding Property using an ObservableCollection tracking the keys when a new Packet gets added (so every key to the Dictionary has an equivalent in this ObservableCollection). I think it's not really a satisfying solution (because you have to keep track of both Collections independently) but it works like this.
I have a ListBox bound to an observable collection of DiceViewModel. Whenever I click a button to add a new item, the ListBox displays the new item like I expect. Everything so far is working well.
<ListBox
ItemsSource="{Binding Path=AllDice}"
DisplayMemberPath="Value"/>
However, I have another button to roll all existing dice. The items already listed in the box don't get updated, and I'm not sure how to enforce this while keeping to the MVVM design pattern.
Also, my DiceViewModel already implements INotifyPropertyChanged.
Any suggestions?
After some more digging around, here's what I've found. The ObservableCollection doesn't automatically register itself with my DiceViewModel's INotifyPropertyChanged event. So any property changes don't get handled.
However, there is a way to do it in the xaml file:
I added this namespace definition to my Window element.
xmlns:vm="clr-namespace:Rolling.ViewModel"
Then I modified my ListBox to use a DataTemplate with a specified DataType:
<ListBox ItemsSource="{Binding Path=AllDice}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type vm:DiceViewModel}">
<TextBlock Text="{Binding Path=Value}"/>
</DataTemplate>
</ListBox.Resources>
</ListBox>
With the specified DataType, the ObservableCollection could register itself with my collection items, receive their events, and then fire it's own CollectionChanged event.
I hope this helps some other people with this poorly documented feature.
You need to implement the INotifyCollectionChanged interface on the collection that the items are bound to and then have it fire the CollectionChanged event to indicate that the collection changed.
This will cause a refresh of the entire list.
In the case of ObservableCollection INotifyPropertyChanged will only notify on changes to the structure of the collection, generally this is the addition and removal of items. The collection has no knowledge of changes to the properties of an individual item within the collection. Instead that individual item itself is responsible for sending notification of its properties changing.
The reasoning behind this goes back to class responsibility and separation of concerns. Since the DiceViewModel likely has data related rolling a die and the value of its last roll then it would follow that it would send notification when its own properties change.
Basically, I have a list of colors and a defined datatemplate for listbox item:
<DataTemplate x:Key="colorItemDataTemplate">
<Border x:Name="borderInner" BorderBrush="Black" BorderThickness="1" Background="{Binding Brush}" Width="11" Height="11" />
</DataTemplate>
Now, when I add a bunch of items into the listbox and then set the ListBox.ItemsSource property to my List, the listbox is filled correctly.
There is also a slider with its appropriate event handler. Within the event handler, Brush property of one of the items from the listbox is changed. Since the appearance of the item depends on the Brush Property, listbox should reflect the change.
I could reset the ItemsSource property, but then all items have their templates applied and with more than 200 items in the listbox, this is pretty slow.
So, is there any way to refresh the template for only one item from the listbox?
Thanx
I'm not sure I follow. If you've bound the Background to the property, changing the property should automatically udpate the background of the ListBoxItem. If you're not seeing that, make sure you are either using a DependencyProperty or implementing INotifyPropertyChanged.
You could use a binding converter. In the converter class you could have some logic like
(pseudo-code)
if (ListBoxItem.IsSelected)
return SpecialColorFromSlider
else
return NormalListBoxColor