WPF ListView with group of RadioButtons and select default value - c#

Today I have a problem with selected default CheckBox. But First i show my code:
<ScrollViewer>
<ListView ItemsSource="{Binding itemsSource, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<Label Content="{Binding AttrName, Mode=OneWay}" />
</Expander.Header>
<ListView Margin="20, 0, 0, 0" ItemsSource="{Binding subItemSource}" BorderBrush="Transparent" >
<ListView.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="{Binding DataContext.AttrName, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Content="{Binding}"
<!-- What should I bind to to get item checked? -->
IsChecked={}/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
So i have a nested binding where in CheckBoxes I bind GroupName to parent data context. My itemsSource contains the following properties:
int DefaultValue { get; set; }
List<int> subItemSource { get; set; }
And all I want now is to mark RadioButton when actual binding value is equal to DefaultValue. How should I do this? Should I write validator?

I'll start by writing a converter class
class ElementComparer : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values[0] == values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
then declare the converter as a resource where l: is your namespace to converter
<l:ElementComparer x:Key="ElementComparer"/>
then in your data template
<DataTemplate>
<RadioButton GroupName="{Binding DataContext.AttrName, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Content="{Binding}"
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource ElementComparer}" Mode="OneWay">
<Binding Path="DataContext.DefaultValue" RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding />
</MultiBinding>
</RadioButton.IsChecked>
provided the datacontext of the ItemsControl is containing the property for default value to compare with, the trick is to compare the selected item of the list to the current item to detect if it is default item, and will return true from converter and hence radio will be checked

Related

WPF bind enum to combobox

In my WPF UserControl I need to bind an Enum on a ComboBox. This enum is declared locally:
public partial class ViewerDataConfiguration : UserControl
{
private ViewerDataConfigurationViewModel PageViewModel;
public Visibility IsParametriSelected { get; set; }
public IEnumerable<eDatoAlarmMode> EnumAlarmModes {
get
{
return Enum.GetValues(typeof(eDatoAlarmMode)).Cast<eDatoAlarmMode>();
}
}
On the main Grid, where there is a collection bound, I defined a ComboBox as follows:
<TextBox Grid.Column="16" Text="{Binding ConfigObject.Edit.Source}" Style="{StaticResource txtDataStyle2}" Width="30" Visibility="{Binding ConfigObject.Edit, Converter={StaticResource ListaValoriVisibilityConverter}}" HorizontalAlignment="Stretch" TextChanged="Data_TextChanged" />
<Label Grid.Column="17" Content="AlarmMode" Style="{StaticResource labelStyle2}" />
<ComboBox Grid.Column="18" Width="30"
ItemsSource="{Binding Path=EnumAlarmModes, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:ViewerDataConfiguration}}"
DisplayMemberPath="Value"
SelectedValuePath="Value" Style="{StaticResource comboUsersStyle}" />
Basically seems that my IEnumerable is not bound correctly. I see the elements but they're blank. Any hint?
You are using DisplayMemberPath and SelectedValuePath attributes, but your collection item type is just a simple string, and want to use this entire instance directly, so you should remove these attributes and it should work as expected.
You also need to change the field to a property as data binding works only on properties, not class fields (although x:Bind in UWP no longer has this limitation):
public IEnumerable<AlarmMode> EnumAlarmModes
{
get
{
return Enum.GetValues(typeof(AlarmMode)).Cast<AlarmMode>();
}
}
If you want to display enum values instead of names, create a value converter:
public class EnumValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then use it in the ItemTemplate:
<ComboBox Grid.Column="18" Width="100"
ItemsSource="{Binding Path=EnumAlarmModes}">
<ComboBox.Resources>
<local:EnumValueConverter x:Key="EnumValueConverter" />
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource EnumValueConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
For this to work you also have to add a xmlns:local declaration to your Window:
xmlns:local="clr-namespace:NamespaceOfConverter"

Using ObjectDataProvider inside HierarchicalDataTemplate

I want to add items of my class treeviewitem to a TreeView.
And I want to bind the ItemSource of this TreeViewItem to a method of itself !
I am trying to use the ObjectDataProvider for this.. See my XAML:
<Grid Background="#FFE5E5E5">
<Grid.Resources>
<HierarchicalDataTemplate DataType="{x:Type myNs:treeviewitem}">
<HierarchicalDataTemplate.Resources>
<ObjectDataProvider x:Key="getItems"
MethodName="GetItems"
ObjectInstance="{Binding RelativeSource={RelativeSource Self}}" />
</HierarchicalDataTemplate.Resources>
<HierarchicalDataTemplate.ItemsSource>
<Binding Source="{StaticResource getItems}" />
</HierarchicalDataTemplate.ItemsSource>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5,0,0,0"
Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</Grid.Resources>
<TreeView x:Name="guiTreeview"
HorizontalAlignment="Left"
Width="200" />
</Grid>
But binding to an ObjectInstance isnt possible!
How is it possible to get the current object instance "into" the ObjectDataProvider?
What would be the right way of doint this?
And NO, its not possible to use a Property ..
I have done it now with a ValueConverter.
XAML:
<Grid Background="#FFE5E5E5">
<Grid.Resources>
<HierarchicalDataTemplate DataType="{x:Type myNs:MyItem}" ItemsSource="{Binding RelativeSource={RelativeSource Self}, Converter={myNs:GetItemsConverter}}" >
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5,0,0,0" Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</Grid.Resources>
<TreeView x:Name="guiTreeview" HorizontalAlignment="Left" Width="200" />
</Grid>
Converter:
public abstract class BaseConverter : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
public class GetItemsConverter : BaseConverter, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var tvi = value as TreeViewItem;
if (tvi == null) return null;
var myitem = tvi.DataContext as MyItem;
if (myitem == null) return null;
return myitem.GetItems();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}

C# WPF ListView of Comboboxes; inform user about SelectionChanged of one of the Comboboxes

Thanks for all help in advance!
I have a listview that shows the user a List of ComboBoxes, in which he can choose different units. It looks similar to this:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="ChangedIndicator"
Text="#"
Visibility="{Binding DefaultChanged,
Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="{Binding Path=Name}"
Margin="5"
MinWidth="210"
TextAlignment="Center"/>
<ComboBox ItemsSource="{Binding Units}"
DisplayMemberPath="Symbol"
SelectedValue="{Binding DefaultUnit.Key}"
SelectedValuePath="Key"
Name="UserChangesComboBox">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Symbol}"/>
<TextBlock Text=" ("/>
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=")"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Now, my problem:
As you see, I want to show the user any changes done in the form of a '#' in a textbox next to the Combo. If the user has changed the selection of a certain combobox I want to show the '#'. I'm using the visibilityConverter for this, but thats not the problem.. Basically I want to have a property of Boolean or similar which is true for each combo when SelectionChanged is raised for each of the combos. Or maybe this idea goes into the wrong direction and someone of you can help me out with this..
I want something like this:
(#) Speed -> [m/s]
[km/h]
while the '#' indicates a user change in this combobox! And the [] is basically the combobox with it's two choices..
But the problem is that all comboboxes are objects in an ObservableCollection..
Thanks a lot for any help!
I think the way you do is create DefaultChanged bool property in the same viewmodel which holds the combobox items collection also with SelectedItem property.
Whenever the selectedItem changes you will set that DefaultChanged to true and the '#' will appear in your view.
If you merely want to indicate the changed textbox, I would use a multibinding on the visibility property of the textbox. This works:
<ListView Name="LV_Items">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="#">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource NonEqualValuesToVisibilityConverter}">
<Binding Path="ComboDefaultValue" Mode="OneWay"></Binding>
<Binding ElementName="CB_Selection" Path="SelectedValue" Mode="OneWay"></Binding>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
<ComboBox Name="CB_Selection"
ItemsSource="{Binding ComboItems}"
DisplayMemberPath="Item2"
SelectedValue="{Binding ComboDefaultValue, Mode=OneTime}"
SelectedValuePath="Item1">
</ComboBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
code for the multivalue converter:
public class NonEqualValuesToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values == null || values.Length != 2)
{
return Visibility.Hidden;
}
return values[0].Equals(values[1]) ? Visibility.Hidden : Visibility.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The listview is bound to collection of items like so:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var items = new List<MyListViewItemData>();
items.Add(new MyListViewItemData()
{
ComboDefaultValue = 1,
ComboItems = new List<Tuple<int, string>>(){ new Tuple<int, string>(1, "m/s"), new Tuple<int, string>(2, "km/h") }
});
items.Add(new MyListViewItemData()
{
ComboDefaultValue = 10,
ComboItems = new List<Tuple<int, string>>() { new Tuple<int, string>(10, "seconds"), new Tuple<int, string>(20, "minutes") }
});
LV_Items.ItemsSource = items;
}
}
public class MyListViewItemData
{
public List<Tuple<int, String>> ComboItems { get; set; }
public int ComboDefaultValue { get; set; }
}
I chose an arbitrary class MyListViewItemData to store all possible values and the default value.

MultiBinding Converter in CheckBox.IsChecked not called

I have a custom combobox a multiselectioncombobox if you will,
the thing is the selections depend on an other collection. I tried to bind ComboBox.IsChecked property to MultiBinding Converter but the converter isn't called.
<DataTemplate>
<StackPanel Orientation="Horizontal" x:Name="ItemStack" VirtualizingStackPanel.IsVirtualizing="False">
<CheckBox x:Name="CheckBoxItem"
Command="{Binding SelectItem, RelativeSource={RelativeSource AncestorType={x:Type MultiSelectionComboBox}}}"
CommandParameter="{Binding Key}"
>
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource MultiSelectionCommandConverter}" Mode="OneWay">
<Binding Path="Key"/>
<Binding Path="SelectedItem"
RelativeSource="{RelativeSource AncestorType={x:Type MultiSelectionComboBox}}" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
<TextBlock Text="{Binding DisplayText}"></TextBlock>
</StackPanel>
</DataTemplate>
and the converter is,
public class MultiSelectionCommandConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
///stuff to do...
}
public object[] ConvertBack(object values, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
any suggestions?
After trying out possibilities, I've found a work around. Still I'm not quite sure why this might work and the other won't.
I've changed my xaml to pass the whole object instead of the property. So the code looked liked this,
<DataTemplate>
<StackPanel Orientation="Horizontal" x:Name="ItemStack" VirtualizingStackPanel.IsVirtualizing="False">
<CheckBox x:Name="CheckBoxItem"
Command="{Binding SelectItem, RelativeSource={RelativeSource AncestorType={x:Type MultiSelectionComboBox}}}"
CommandParameter="{Binding Key}"
>
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource MultiSelectionCommandConverter}" Mode="OneWay">
<Binding Path="Key"/>
<Binding
RelativeSource="{RelativeSource AncestorType={x:Type MultiSelectionComboBox}}" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
<TextBlock Text="{Binding DisplayText}"></TextBlock>
</StackPanel>
</DataTemplate>
and the converter is
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string key = (string)values[0];
ObservableCollection<ListItem> selectedItems = (values[1] as MultiSelectionComboBox).SelectedItem;
//do stuff
return false;
}
This is definitely not a desired solution but, this will do until i figure out the other reason.

How to get datacontext from stackpanel

I have:
<StackPanel DataContext="{Binding Path =MyContext}">
<TextBox Text="{Binding Path =Content}" x:Name="tbName" IsReadOnly="False">
</TextBox>
<CheckBox x:Name="cboxName" Content="Is null ?" Click="cboxName_Click" IsChecked="{Binding Path=THIS, Converter={StaticResource MyContextToBoolConverter}}">
</CheckBox>
</StackPanel>
public class MyContextToBoolConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value!=null);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return parameter;
}
}
I just only want to get DataContext to checkbox from StackPanel.
You should replace THIS with . or completely remove the Path from the Binding. This will create a binding directly to the DataContext.
IsChecked="{Binding Converter={StaticResource MyContextToBoolConverter}}"
Or try this -
<StackPanel x:Name="StackPanel" DataContext="{Binding Path =MyContext}">
<TextBox Text="{Binding Path =Content}" x:Name="tbName" IsReadOnly="False" />
<CheckBox x:Name="cboxName" Content="Is null ?"
Click="cboxName_Click"
IsChecked="{Binding ElementName=StackPanel, Path=DataContext, Converter={StaticResource MyContextToBoolConverter}}">
</CheckBox>
</StackPanel>

Categories