SelectedIndex DataTrigger not working in Combobox - c#

What I want to do is if a combobox has only 1 item then it gets preselected. I tried usind DataTriggers but its not working for 'SelectedIndex' property.But when I set 'IsEnabled' property to false its working and disable the combobox.
Below is my code:
<ComboBox Name="WarehouseCombo"
ItemsSource="{Binding Path=WarehouseList}"
SelectedValue="{Binding Warehouse,Mode=TwoWay}"
Text="{Binding Path=TxtWarehouse,Mode=TwoWay}">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=Items.Count, ElementName=WarehouseCombo}" Value="1">
<Setter Property="SelectedIndex" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
Please help me as why is this happening in 'SelectedIndex' case.

Don't use the SelectedIndex property. Instead, use the SelectedItem property. One way to fulfil your requirements is to declare a base class for your data collection. Try something like this:
public WarehouseList : ObservableCollection<WarehouseItems>
{
private T currentItem;
public WarehouseList() { }
public T CurrentItem { get; set; } // Implement INotifyPropertyChanged here
public new void Add(T item)
{
base.Add(item);
if (Count == 1) CurrentItem = item;
}
}
Now if you use this class instead of a standard collection, it will automatically set the first item as selected. To make this work in the UI, you simply need to data bind this property to the SelectedItem property like this:
<DataGrid ItemsSource="{Binding WarehouseList}"
SelectedItem="{Binding WarehouseList.CurrentItem}" />

Related

Incorrect size of the scroll bar in the list view

Hell all. I work on some desktop application that contains some UI windows created by WPF. One of these windows has a table. Each of the headers in this table can open our custom control for filtering. This custom control contains the ListView element with binding to some collection of items. And when I change the state of some items in this collection the scroll bar in the ListView scrolling incorrectly.
Here is the class that I use to represent the info about each item in the ListView:
public class ItemInfo : ViewModelBase
{
public bool IsSelected
{
get => isSelected;
set
{
Set(ref isSelected, value);
}
}
public bool IsAvailable
{
get => isAvailable;
set
{
Set(ref isAvailable, value);
}
}
public string Name
{
get => name;
set
{
Set(ref name, value);
}
}
private string name;
private bool isSelected = true;
private bool isAvailable = true;
}
Each of the items in the ListView contains the checkbox. The IsSelected property indicates the state of this checkbox. The IsAvailable property indicates if the item in the ListView will be visible or hidden.
Here is the code for the ListView:
<ListView ItemsSource="{Binding CollectionForFiltering}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsAvailable}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox IsChecked="{Binding IsSelected}">
<CheckBox.Content>
<Label Content="{Binding Name}"/>
</CheckBox.Content>
</CheckBox>
</HierarchicalDataTemplate>
</ListView.ItemTemplate>
</ListView>
The CollectionForFiltering is described as:
List<ItemInfo> items = new List<ItemInfo>(... some collection of items ...);
ICollectionView CollectionForFiltering = CollectionViewSource.GetDefaultView(items);
In some cases, I change the IsAvailable property for some items in the collection. And these items will be hidden in the ListView. But when I try to scroll the list I see some changes in the scrollbar size during the scrolling. Please, see the attached gif:
How can I fix that? How can I refresh/update the scrollbar after some items are hidden?

Change ComboBox ItemsSource based on another ComboBox selection

I have two ComboBoxesA and B. First one is populated with a list of items.
I need to change the ItemsSource Binding of the B based on A's SelectedItem
Issue: When X is selected on A, B is not getting populated.
Please note that I'm not filtering the ItemsSource, it a complete change of binding. myItemSources are ObservableCollections
<ComboBox Name="ComboBoxB">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding myItemSource1}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue, ElementName=ComboBoxA}" Value="X">
<Setter Property="ItemsSource" Value="{Binding myItemSource2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
There is an example of how to implement this kind of cascading ComboBoxes using the MVVM design pattern available here: https://blog.magnusmontin.net/2013/06/17/cascading-comboboxes-in-wpf-using-mvvm/
<ComboBox ItemsSource="{Binding Countries}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedCountry}" />
<ComboBox ItemsSource="{Binding Cities}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedCity}"
Margin="0 5 0 0"/>
public CustomObservableCollection<City> Cities {
get;
private set;
}
private Country _selectedCountry;
public Country SelectedCountry {
get {
return _selectedCountry;
}
set {
_selectedCountry = value;
this.Cities.Repopulate(_selectedCountry.Cities);
}
}
...
}

Refresh a single item in a WPF ItemCollection

I have a DataGrid that is bound to some XML data.
When I make changes in the XML data, the DataGrid does not refresh to reflect those changes.
My "simple" way of fixing this is to call MyDataGrid.Items.Refresh() every time I make a change.
However, this is laggy and seems pretty inefficient.
How can I refresh just a single row, rather than the entire data grid? I have easy access to the DataGridRow as well as the XmlElement that is changed, but I just don't know what function to call.
Been stuck on this problem for 3-4 hours now and have tried dozens of solutions, but just can't get it to work.
Below is relevant code.
A) Defining the style.
<!-- Field Value Style -->
<local:FieldValueConverter x:Key="FieldValueConverter"/>
<local:Node x:Key="Node"/>
<Style x:Key="fieldValueStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding ., Converter={StaticResource FieldValueConverter}}"/>
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
<Setter Property="Focusable" Value="False"/>
</Style>
B) Defining the DataGrid
<DataGrid x:Name="FieldPanelDataGrid" DockPanel.Dock="Left"
AutoGenerateColumns="False"
DataContext="{Binding ElementName=ObjectPanelListBox, Path=SelectedItem}"
ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"
IsReadOnly="True"
CanUserResizeRows="False"
CanUserResizeColumns="True"
KeyboardNavigation.IsTabStop="False"
Visibility="Visible"
SelectionMode="Single">
<DataGrid.Resources>
<Style TargetType="DataGridRow">
<EventSetter Event="MouseDoubleClick" Handler="FieldCell_MouseDoubleClick"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="FieldCell_PreviewMouseLeftButonDown"></EventSetter>
<EventSetter Event="PreviewKeyDown" Handler="FieldCell_PreviewKeyDown"></EventSetter>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn x:Name="FieldName" Header="Name" CanUserSort="False" ElementStyle="{StaticResource fieldNameStyle}"/>
<DataGridTextColumn x:Name="FieldValue" Header="Value" Width="*" ElementStyle="{StaticResource fieldValueStyle}"/>
</DataGrid.Columns>
</DataGrid>
I suggest to use an ObservableCollection as ItemSource and the entries in the ObservableCollection have to implement INotifyPropertyChanged. Then you have the benefit if the rows change, the ObservableCollection will tell that your UI and it will update.
Example:
Your entry class:
public class MyXmlObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string fieldName;
public string FieldName
{
get { return fieldName; }
set
{
fieldName = value;
NotifyPropertyChanged("FieldName");
}
}
NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Your code for the UserControl (ViewModel, Controller or Code behind):
public ObservableCollection<MyXmlObject> MyCollection { get; set; }
And as I mentioned in your xaml you simply bind the collection to the ItemsSource
<DataGrid ItemsSource="{Binding MyCollection}" .../>
Now only the items beeing changed get updated.

WPF switch binding with trigger

I'm trying to switch the ItemsSource property on a ComboBox via triggers on a checkbox. Here is my code:
<CheckBox Content="Test" VerticalAlignment="Center" Margin="5,0,0,0">
<CheckBox.Triggers>
<Trigger Property="CheckBox.IsChecked" Value="True">
<Setter TargetName="MyComboBox" Property="ComboBox.ItemsSource" Value="{Binding A}" />
</Trigger>
<Trigger Property="CheckBox.IsChecked" Value="False">
<Setter TargetName="MyComboBox" Property="ComboBox.ItemsSource" Value="{Binding B}" />
</Trigger>
</CheckBox.Triggers>
</CheckBox>
As you can see, the intended purpose is to switch between binding "A" and binding "B" depending on the checkbox's IsChecked state. I've seen a lot of people put these triggers in a Style, but that gets rid of my window theme, which I want to keep. Additionally, I'd like this to be in XAML only as I need to apply this sort of binding switch to multiple combo box / checkbox pairs in my application.
The problem I'm having is that when I put in the above code my application crashes on startup! I've isolated it to the trigger code above (removing that removes the crash). Any help is appreciated!
I'm guessing that MyComboBox is not contained in the CheckBox and is therefore out of the naming scope that the Trigger is defined.
Instead of adding the trigger to the CheckBox, why not add it to the ComboBox and binding the CheckBox.IsChecked property to a property in your view model, like so:
<CheckBox IsChecked="{Binding ShowComboBoxItemsA}"/>
<ComboBox ItemsSource="{Binding A}">
<ComboBox.Triggers>
<DataTrigger Binding="{Binding ShowAComboBoxItems}" Value="False">
<Setter Property="ItemsSource" Value="{Binding B}"/>
</DataTrigger>
</ComboBox.Triggers>
</ComboBox>
The other option would be bind the CheckBox.IsChecked property to a property in your view model, as in the first, but then in your setter update the value of the ComboBoxItems.
<CheckBox IsChecked="{Binding ShowComboBoxItemsA}"/>
<ComboBox ItemsSource="{Binding ComboBoxItems}"/>
public List<object> ItemsA { get; set; }
public List<object> ItemsB { get; set; }
bool showComboBoxItemsA;
public bool ShowComboBoxItemsA
{
get { return showComboBoxItemsA; }
set
{
if (showComboBoxItemsA != value)
{
showComboBoxItemsA = value;
OnPropertyChanged("ShowComboBoxItemsA");
if (showComboBoxItemsA)
ComboBoxItems = ItemsA;
else
ComboBoxItems = ItemsB;
}
}
}
List<object> comboBoxItems;
public List<object> ComboBoxItems
{
get { return comboBoxItems; }
set
{
comboBoxItems = value;
OnPropertyChanged("ComboBoxItems");
}
}

Binding IsEnabled on ComboBoxItem does not work

I have this model:
public class Option : BindableBase
{
private bool _enabled;
public bool Enabled
{
get
{
return _enabled;
}
set
{
SetProperty(ref _enabled, value);
}
}
private string _value;
public string Value
{
get
{
return _value;
}
set
{
SetProperty(ref _value, value);
}
}
}
In my viewmodel, I got a list Options of type ObservableCollection<Option>.
I use this XAML piece of code:
<ComboBox Width="200"
ItemsSource="{Binding Options}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Value}"/>
<TextBlock Text="{Binding Enabled}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnabled" Value="{Binding Enabled}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
I add some Option in my list, some of them have the Enabled property set to true while others do not.
However, the ones having the property to false are still enabled in the ComboBox and I can select them (while I should not!). When using this line of code:
<Setter Property="IsEnabled" Value="false"/>
The options are effectively disabled (I can't select any of them) but I'd like to use some binding. Can someone explain what I'm doing wrong here?
You are setting IsEnabled on the content of your ComboBoxItem while you would need to set it on the ComboBoxItem itself. You would need to set the binding in the ItemContainerStyle. I'm not sure if bindings are supported in style setters though. If they are not, then you might need to use a workaround to set up the binding.
Maybe you need to use path instead. You can try this :
<Setter Property="IsEnabled" Value="{Binding Path=Enabled}"/>

Categories