Combobox trigger not working to set selected index - wpf

I have a situation where i need to set SelectedIndex of my combobox to 0 in case the selectedItem gets deleted from the ItemsSource collection. A small sample replicates this situation well -
Xaml
<Grid>
<ComboBox x:Name="cmb" ItemsSource="{Binding Names}" VerticalAlignment="Top">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<Trigger Property="SelectedValue" Value="{x:Null}">
<Setter Property="SelectedIndex" Value="0"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
<Button VerticalAlignment="Bottom" Width="100" Height="20" Click="Button_Click"/>
</Grid>
I haven't binded my SelectedItem or SelectedIndex to any value. On load, trigger works fine and set the SelectedIndex to 0 but on button click, i am deleting the current selectedItem from Names collection. Hence, the selected value is set to null. But the trigger not fires in that case and combobox comes as blank.
I know it can be solved in code behind by listening to SelectionChanged event and setting the SelectedIndex back to 0 there in case currentItem is null. But, i can't use that approach because of some restrictions. I want some pure xaml approach. Thanks in advance..
EDIT
Setting IsSynchronizedWithCurrentItem to True for combobox, prevents combobox to be empty but on removing the selectedItem from ItemSource collection, it set the next available value to be selected but i want my first item to be selected.

I think it's because you can't have the same value inside the trigger as what the trigger is based on, and essentially SelectedValue, 'SelectedItem', and SelectedIndex are just different ways to refer to the same item.
You'll note if you add a background color to the trigger, it gets applied, so the trigger is definitely working.
I'm not sure what restrictions you have which you say are preventing you from using SelectionChanged, however I often see people say stuff like that because they are new to the MVVM design pattern, and think it means an application should have no code-behind at all. That is incorrect.
Using MVVM means that there should be no business logic in the code-behind. UI logic is perfectly acceptable in the code-behind a View, and I would consider this to be UI logic because the only thing it is doing is altering the UI based on a UI value.

Since you are anyway deleting the selected item in the button_Click, Could you not accomplish what you want by setting the selectedIndex value to zero inside the Button_Click(sender, eventargs) handler after you delete the current selected item!

Related

WPF MVVM: How to enable/disable buttons in ListBox if I'm using a DataTemplate

I have a WPF/MVVM app with a ListBox which displays data through a DataTemplate. I managed to change the selected item in the ListBox when pressing a button so the CommandParameter is linked to the ListBox's SelectedItem, but I cannot get the buttons to be enabled/disabled correctly in the same way. For example, if I have 2 items and the button should be enabled in one and disabled in the other, when I select an element BOTH buttons have the same state, and they BOTH change state when I select another item.
I am using a RelayCommand as used in many MVVM Frameworks.
Here is my XAML (removed "not interesting" parts):
<UserControl.Resources>
<DataTemplate x:Key="ItemTemplate">
<Grid>
<Button Content="Something" Name="EnabledDisabledButton" Click="Button_Click"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SomeCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=SelectedItem}"/>
</Grid>
</DataTemplate>
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />
</Style>
</UserControl.Resources>
<ListBox x:Name="myListBox" ItemsSource="{Binding ElementList}"
IsSynchronizedWithCurrentItem="True" ItemContainerStyle="{StaticResource ContainerStyle}"/>
I tried to pass the SelectedItem as a parameter to the RelayCommand's CanExecute method, but the result was the same as before.
Is there a way to pass the actual ListBoxItem in which the button "lives in" as a parameter to the command, so each one will be processed separately by the CanExecute method? Would it work if I got this? (right now I am handling the Click event to select the correct item in the list before executing the command).
In my CanExecute method I am evaluating some property of the SelectedItem in order to enable/disable the corresponding button. An alternative would be to evaluate this property for all elements, but I cannot think of a way to do it inside the ViewModel, and then communicate to the view the result (if it is even possible while using a DataTemplate for the items).
Thanks for your input, regards!
Converting My comment into an answer:
Why not just CommandParameter="{Binding}"?
You mention "MVVM" in the question, but it seems you use the MVVM way to your full advantage.
I would not have a Button_Click event in the style at all. That is because it is in fact a style, which per definition could be changed to another style which does not have the same event, which again will make the application stop working as wanted if you choose to have a style-based app in the future.
A rule I use is that a style is a style. A style has to do with the UI and "looks" of the app.
Functionality should be separate from the UI. The programmer can define the Command, and the designer can decide how the user will use that in the best way.
That's exactly where the code separation from the MVVM pattern cames into grip.
To separate the "looks" and user behavior and the app's logic.
Like...it should not matter to the model if a command fires from a button, a menu, a datacontext or a key stroke.
If this particular problem was handled to ME, I would solve it by having a HOLDER-class.
This is a class (DependencyObject which implements INotifyPropertyChanged) that holds a ICommand property as well as the "row" that will be displayed in the various rows in the ListBox.
The ICommand property will be bound to the Button, having the row (class) itself as CommandParameter to the call.
Then the actual row would be used in the ItemTemplate on the ListBox, with Bindings to different elements (proprty with or withouy Converters) to make whatever desired display available.
I hope I explained good enough...
Feel free to ask more if you want more details to my solution alternative.

How does DataGrid.SelectedIndex behave when

I have a WPF data grid with multi select (SelectedMode = Extended). Each item has an IsSelected binding per https://stackoverflow.com/a/2615487/284795
<DataGrid
ItemsSource="{Binding Items}"
SelectionUnit="FullRow"
SelectionMode="Extended"
SelectedIndex="{Binding SelectedIndex}"
SelectedItem="{Binding SelectedItem}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
I'm curious. How do the properties SelectedIndex and SelectedItem on the DataGrid now behave? If two items are selected in the data grid, which one does SelectedItem point to?
Also, if all these bindings are two way, and I make a change to one from a view model, will the others be updated? (I'm observing a bug in my app perhaps because of this)
I had the same question a while ago, and I checked: I created a DataGrid with several items and set the SelectedMode = Extended, and I mde a binding to both: SelectedItem and SelectedIndex properties.
The result was this: When you select a single item, and then you select other items and make a multiselect, the SelectedItem and SelectedIndex properties will be the first item that you selected. So when you make multiselects the selected item will be the first one you selected.
Also all other selected items will be in the SelectedItems collection, that is read only (like in the question you pointed said) and it is not a dependency property, so you can't make bindings to it. So if you want take all selected items, you need to handle the selection changed event of the DataGrid and then, manually, add and remove the new and old items from the collection that you want to keep (selected items).
Hope my answer be clear enough, and it could helps you...

How to set SelectedItem of a ComboBox when item is not part of the ItemsSource collection?

Following scenario:
<ComboBox ItemsSource="{Binding Path=Names}"
SelectedItem="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}">
</ComboBox>
Names is a string collection which might change through code in the ViewModel. For example Names could be set to either male names (John, Paul, George) or female names (Mary, Jane, Karen).
Let's assume the male collection is active and the user selects "John" making it the SelectedItem. Now the collection changes to female names. By default the SelectedItem will be set to Null and John will not be displayed anymore. Instead an empty string will be displayed.
What I would like to achieve is that "John" is still visible as SelectedItem although it is not in the collection anymore. However as it is not part of the collection I would like to change the color of the SelectedItem to red.
I have played around with the IsEditable property, but didn't like the changing appearance of the ComboBox. The user should not be able to enter text manually anyway.
I tried to use different templates and styles (e.g. Template, ItemTemplate, ItemContainerStyle), but didn't find a way to use it to my favour.
Is it possible to style the provided ComboBox the way I need it or do I have to create my own user control?
EDIT:
I have found a solution I can live with.
<ComboBox IsEditable="True" IsReadOnly="True"
ItemsSource="{Binding Path=Names}"
Text="{Binding Path=Name}">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsNameInCollection}" Value="False">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Foreground" Value="Black"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
In the ViewModel I make sure that the Name property does not change when the Names collection changes. The boolean property IsNameInCollection will be updated when the Name property or the collection changes.
Note: this no a real, working answer / solution, but just some thoughts you might find worth to consider:
First, the SelectedItem will always change to null if the collection changes (either by replacing it with a new collection or clearing and refilling it). So as you are binding to Name property of some object, can you prevent that property from being overridden with null there internally? That way the value might not change in the ComboBox (maybe you also need TwoWayBinding for Name to make the value stick; or even firing a change event, but that depends on some testing).
Second, the rendering needs to change, so the easiest way would be to use the ItemTemplate and a custom IValueConverter implementation to convert the information of the special entry with different color / style. But the downside of this is, that the ItemTemplate will normally be used for every entry, including the current selected.
One way to deal with this is to use an ItemTemplateSelector like described here: http://www.wpftutorial.net/datatemplates.html (section "How to use a DataTemplateSelector to switch the Template depending on the data")
Another way would be to use a container instead of plain string. That container would also hold a boolean value to indicte a different rendering. That boolean value could be used in the ItemTemplate. But in this scenario you need to wrap all string values.
Third, how should the ComboBox react if the selected value is still the old value that is not in the current list, and then the user selects another value. One logic would be that the old value just disappears, but then the user cannot select it back. If it should be still available in the list, then you must somehow add it to the list of current values, but mark it that it is a special value. In both cases you also need a logic that detects later if a value was chosen that is not valid according to the current available list.
I hope this helps to find a working solution.

Losing the binding for radiobutton after I set the property manually in code

I have a list on my WPF xaml which contains two items. Below is the Style template for each item. Now on UI this shows like a group of radio buttons(No. of radio buttons depends on no. of items in my list).
<Style x:Key="RadioButtonListBoxItemStyle" TargetType="{x:Type ListBoxItem}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<RadioButton FlowDirection="LeftToRight"
Margin="10 15"
Content="{Binding Value}"
GroupName="{Binding DisplayGroupName}"
IsChecked="{Binding IsSelected, Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I bind a list(having 2 items) using the above style template to get two radio buttons. What happens is everything works pretty fine i.e when I change the selection of radio button on UI the IsSelected property is getting updated properly to true or false depending on whether my radio is checked/un-checked. But if I try to set the list in the code manually, then from that point my binding of the radio button's with my list is lost and nothing happen's.
Any help on this would be great and based on my needs I have to set the list in the code manually. So is there any solution in a way that binding will not be lost even though I set the list in my code manually. Thanks.
-Ady.
This is a common problem with radio buttons in WPF, and it has to do an unusual aspect of binding, one that is marginally more feature than bug.
The design of binding assumes that the only two things that change the value of a binding's target property are a) actions in the UI and b) changes to the source property. If you set the target property of a binding in code - like, you explicitly set the Background of a Border, even though it has a binding - the binding decides that you know what you're doing, and that it should just get out of the way. So it turns itself off.
This is a pretty sensible design decision, for the most part. It's better than throwing an exception, for instance. Most of the time, you're not going to ever set IsEnabled in code anyway; you'll let the binding do it. Especially if you're using MVVM.
Okay, so what happens if you have radio buttons in a group?
When you check one button in the group, the WPF code that manages radio button groups unchecks all the other buttons in the group, by setting IsChecked to false in code. The binding disables itself. Oops.
Here's the solution: If you're using radio buttons and binding, don't use groups. Handle the mutual exclusion logic in your view model code. In your case, code your view models so that only one object in a collection can have IsSelected true at any given time. (Yes, this is a pain.)
The radio buttons will still work as expected, but since the only properties being set by code are the source properties, binding won't break.
you are setting the style for the listboxitem class, including the bindings. so, when you set the list from code behind it does not contain listboxitems, it contains the items from your list. so, the style does not apply. what you should do is make the <DataTemplate> for the type of item in your list--in effect telling WPF what you want each item to look like.
<DataTemplate TargetType="{x:Type MyCustomClass}" >
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Deleteable, Mode=TwoWay}" />
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</DataTemplate>
(this is off the top of my head, so the xaml might not be exactly right)

WPF/MVVM: Disable a Button's state when the ViewModel behind the UserControl is not yet Initialized?

I have a DocumentListView.Xaml with a ListBox and 3 Buttons.
Behind that UserControl sits a DocumentListViewModel with 3 Buttons and their Command Property bound to 3 RelayCommands.
I have 3 Controller like AdministrationController, BillingController, ReportController.
Every Controller has ObservableCollections like Customer 1 : N Order 1: N Document same for the other Controller.
In one Controller I have a special binding situation. When my DocumentListViewModel is not initialized by its parent ViewModel like OrderViewModel (because no orders are loaded/exist) then my UserControl has 3 buttons which are ENABLED. Ok the user can press the 3 buttons and nothing happens but still its very confusing and above all the consistency in my user interface is gone.
How can I set the Command of a Button as default to "Disabled" ?
Setting the Buttons IsEnabled property to false does not help because the button will stay forever in the disabled state. No CanExecute TRUE will set it to IsEnabled = true.
AND I do not want to introduce another property IsButtonEnabled... that stupid because then I have both worlds winforms and wpf behind my buttons logic... ICommand should be enough.
Or you can use a Style for the button to disable:
<Style TargetType="{x:Type Button}" x:Key="DisablerButton">
<Style.Triggers>
<Trigger Property="Command" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</Trigger>
</Style.Triggers>
</Style>
This is an interesting situation. Honestly I've never run into the case where the UI was loaded and interactive but the ViewModel was not yet bound.
However, ignoring that for a moment, you could potentially use a FallbackValue on your binding to bind to a globally available NullCommand or something that always returns false for its CanExecute method.
<Button Command="{Binding SaveCommand, FallbackValue={StaticResource NullCommand}}" />

Resources