Bind IsChecked of a CheckBox to a method - wpf

Is it possible to bind the IsChecked property of a checkbox to a custom method?
I created a list of checkboxes bound to a collection of objects. I have a second collection of objects which is a subset of the first one. I'd like to bind the IsChecked porperty of the checkbox to a method that determines if the object is contained in the second list or not
EDIT:
<ListBox Height="auto" HorizontalAlignment="Stretch" Name="listBox" VerticalAlignment="Stretch" Width="auto" ItemsSource="{Binding DataSources}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="CheckBoxZone"
Content="{Binding Name}"
Tag="{Binding Id}"
Margin="0,5,0,0"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

You can bind the checkbox Command property to a ICommand on your model. This means every time the check is changed the command will be invoked.
Example:
<CheckBox Name="CheckBoxZone"
Content="{Binding Name}"
Tag="{Binding Id}"
Margin="0,5,0,0"
Command={Binding CheckBoxChangedCommand}
/>

You may bind IsChecked to both the data object and the subset collection by means of a MultiBinding in conjunction with a multi-value converter that converts into a bool (or Nullable<bool> for IsChecked) value:
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource ObjectInListConverter}" Mode="OneWay">
<Binding />
<Binding Source="{StaticResource SubsetCollection}" />
</MultiBinding>
</CheckBox.IsChecked>
The converter:
class ObjectInListConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
IList subset = values[1] as IList;
Nullable<bool> result = subset.Contains(values[0]);
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

In addition to Pop Catalin's answer, you will want to bind IsChecked to a property in the VM and modify that VM property when command is executed.

Related

Bind DataContext Index to Slider Control Value

Is it possible to use the current value of a WPF Slider control (Slider.Value) as an input to a Indexing Binding on another control?
Use case: A collection of items is set as the DataContext for a control, and the slider is used to select which item from a collection is displayed.
<Slider x:Name="selector" Minimum="1" Maximum="{Binding Count}"/>
<!-- How to grab the value of selector and use as indexer?? -->
<StackPanel DataContext="{Binding [??????]}">
<TextBlock Text="{Binding name}"/>
<TextBlock Text="{Binding job}" />
</StackPanel>
Is it possible to use the current value of a WPF Slider control (Slider.Value) as an input to a Indexing Binding on another control?
No, not directly. ?????? in {Binding [??????]} has to be a compile-time constant.
You could bind to both the DataContext and the Value property of the Slider and use a converter to perform the lookup though:
public class MultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var yourDataContext = values[0] as IDictionary<double, object>; //cast to whatever the type of your DataContext is
double value = (double)values[1];
return yourDataContext[value];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
}
XAML:
<StackPanel>
<StackPanel.DataContext>
<MultiBinding>
<MultiBinding.Converter>
<local:MultiConverter />
</MultiBinding.Converter>
<Binding Path="." />
<Binding Path="Value" ElementName="selector" />
</MultiBinding>
</StackPanel.DataContext>
<TextBlock Text="{Binding name}"/>
<TextBlock Text="{Binding job}" />
</StackPanel>
Declare two dependency properties on your data context: SliderIndex and SelectedItem (you can name them whatever you want, but those are the names I'll use for my answer).
Bind Slider.Value to SliderIndex. Then use a PropertyChangedCallback to update the SelectedItem property based on the new value of SliderIndex. Finally, bind StackPanel.DataContext to SelectedItem.
This is the best way I know of to do this. There is no easy way to bind the two directly since you can't use a variable as an index for collection binding. The other option is to use an IValueConverter or IMultiValueConverter, but the above is cleaner in my opinion.

How do I obtain the Index of a ListViewItem?

I have a list view that uses a collectionsource and wish to set the background colour of the ListViewItems depending upon whether their index in the list matches a value held in the view model. To do this I am binding the background of the listVeiwItem to the value in the ViewModel, and using a converter to determine the background colour. To make this determination the converter needs to be passed the ListViewItem's index. How do I obtain the index using XAML?
Here is the XAML for the data template used by the ListView:
<DataTemplate x:Key="ILMemberTemplate">
<StackPanel Orientation="Horizontal" Background="{Binding Path=ListIndex, Mode=OneWay, Converter={StaticResource ParticipantBackground}, ConverterParameter={???}}">
<TextBlock
Width="200"
TextAlignment="Left"
Foreground="{Binding Path=IsPC, Mode=OneWay, Converter={StaticResource ParticipantColour}}"
Text="{Binding Path=Name, Mode=OneWay}"/>
<TextBlock
Width="40"
TextAlignment="Center"
Foreground="{Binding Path=IsPC, Mode=OneWay, Converter={StaticResource ParticipantColour}}"
Text="{Binding Path=Initiative, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
You can't obtain the index in XAML and pass it as a ConverterParameter. This is not possible. But you can use a multi converter and bind to both the ListIndex and the parent ListViewItem container, e.g.:
class MultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var listIndex = values[0];
//...
ListViewItem item = value as ListViewItem;
ListView lv = FindParent<ListView>(item);
ICollectionView view = lv.ItemsSource as ICollectionView;
IEnumerator e = view.GetEnumerator();
int index = 0;
while (e.MoveNext())
{
if (Equals(item.DataContext, e.Current))
return index;
else
index++;
}
//return some brush based on the indexes..
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null) return null;
var parentT = parent as T;
return parentT ?? FindParent<T>(parent);
}
}
XAML:
<DataTemplate x:Key="ILMemberTemplate">
<StackPanel Orientation="Horizontal">
<StackPanel.Background>
<MultiBinding Converter="{StaticResource multiConverter}">
<Binding Path="ListIndex" />
<Binding Path="." RelativeSource="{RelativeSource AncestorType=ListViewItem}" />
</MultiBinding>
</StackPanel.Background>
<!-- ... -->
</StackPanel>
</DataTemplate>

WPFLocalizationExtension with ItemTemplate

I'm using WPFLocalizationExtension for my WPF app.
I have one ComboBox for languages selection. Item source is an ObservableCollection<KeyValuePair<string, string>> as below:
TITLE_LANGUAGE_ENGLISH : en
TITLE_LANGUAGE_VIETNAMESE: vi-VN
This is my xaml code:
<TextBlock Text="{lex:Loc TITLE_LANGUAGE}"></TextBlock>
<ComboBox Grid.Column="1"
ItemsSource="{Binding AvailableLanguages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{lex:Loc Key={Binding Key}}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
When I run the application, it throws me an exeption as below:
A 'Binding' cannot be set on the 'Key' property of type 'LocExtension'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject
How can I translate the ItemTemplate ?
Thank you,
You could use an IMultiValueConverter together with a MultiBinding, so that you don't loose the ability to update the localization on-the-fly.
<ComboBox ItemsSource="{Binding AvailableLanguages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding>
<MultiBinding.Bindings>
<Binding Path="Key" Mode="OneTime"/>
<Binding Path="Culture" Source="{x:Static lex:LocalizeDictionary.Instance}"/>
</MultiBinding.Bindings>
<MultiBinding.Converter>
<l:TranslateMultiConverter/>
</MultiBinding.Converter>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
And here is the converter:
class TranslateMultiConverter : DependencyObject, IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2)
{
var key = values[0] as string;
if (key == null)
{
return DependencyProperty.UnsetValue;
}
var cultureInfo = (values[1] as CultureInfo) ?? culture;
return LocalizeDictionary.Instance.GetLocalizedObject(key, this, cultureInfo);
}
return values.FirstOrDefault();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The LocalizeDictionary will raise a PropertyChanged event when the app's language will change causing the MultiBinding to refresh the values.
Note that the converter is a DependencyObject too. This is to provide the context to the LocalizeDictionary when calling the GetLocalizedObject method.
you have to bind to the Path Key directly. The TextBlock at DataTemplate points directly to a single KeyValuePair object, that you can access the property Key directly.
<TextBlock Text="{lex:Loc TITLE_LANGUAGE}"></TextBlock>
<ComboBox Grid.Column="1"
ItemsSource="{Binding AvailableLanguages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Update:
Maybe you have to add a Converter. Try WPFLocalizeExtension.TypeConverters.DefaultConverter or implement a class deriving from IValueConverter by yourself.
<ComboBox.Resources>
<cv:DefaultConverter x:Key="DConv" />
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key, Converter={StaticResource DConv}}" />
</DataTemplate>
</ComboBox.ItemTemplate>

How to reference another control in a TreeViewItem through a binding converter?

I would like to bind the Visibility of a TextBox based on SelectedItem of a ComboBoxin same TreeViewItemContainer. I think I can use a Converter for the Binding but I don't know how to send the ComboBox item as a parameter of the TextBox Binding. Can this be done?
<TreeView>
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox Margin="2,0" Name="SkillSelectCB" ItemsSource="{Binding PotentialChildren}" />
<TextBox Margin="2,0" Width="50" Visibility="{Binding ??}" />
<Button Margin="2,0" Content="Add" />
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
This is actually in a HierarchicalDataTemplate, the example above is very minimal. The "Add" Button will add new children to the ViewModel for the TreeView based on what's selected in the ComboBox. And the visibility is the TextBox will change depending on some property of the ComboBox's SelectedItem.
So the Xaml for the TextBox:
<TextBox Margin="2,0"Width="50" Visibility="{Binding SelectedItem, ElementName=SkillSelectCB, Converter={StaticResource SkillToVisibilityConverter}}" />
And the Converter:
public class SkillToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = (Skill)value;
return (s == null || !s.Specialized) ? "Hidden" : "Visible";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF DataBinding where the Path is recovered from the object?

I have an object with several properties. Two of these are used to control the width and height of the target text box. Here is a simple example...
<DataTemplate DataType="{x:Type proj:SourceObject}">
<TextBox Width="{Binding ObjWidth}" Height="{Binding ObjHeight}"/>
</DataTemplate>
I also want to bind the Text property of the TextBox. The actual property to bind against is not fixed but instead is named in a field of the SourceObject. So ideally I would want to do this...
<DataTemplate DataType="{x:Type proj:SourceObject}">
<TextBox Width="{Binding ObjWidth}" Height="{Binding ObjHeight}"
Text="{Binding Path={Binding ObjPath}"/>
</DataTemplate>
Here the ObjPath is a string that returns a path that would be perfectly valid for the binding. But this does not work because you cannot use a binding against the Binding.Path. Any ideas how I can achieve the same thing?
For more context I will point out that the SourceObject is user customizable and hence the ObjPath can be updated over time and hence I cannot simply put a fixed path in the data template.
You could implement an IMultiValueConverter and use this one as BindingConverter for your Text Property. But then you have the problem, that the value of the Textbox is only updated if your ObjPath property changes (the path itself), not the value where the path is pointing to. If that's, okay you can go with a BindingConverter which returns the value of your binding Path using Reflection.
class BindingPathToValue : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value[0] is string && value[1] != null)
{
// value[0] is the path
// value[1] is SourceObject
// you can use reflection to get the value and return it
return value[1].GetType().GetProperty(value.ToString()).GetValue(value[1], null).ToString();
}
return null;
}
public object[] ConvertBack(object value, Type[], object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Have the converter in your Resources:
<proj:BindingPathToValue x:Key="BindingPathToValue" />
and use it in XAML:
<DataTemplate DataType="{x:Type proj:SourceObject}">
<TextBox Width="{Binding ObjWidth}" Height="{Binding ObjHeight}">
<TextBox.Text>
<MultiBinding Mode="OneWay" Converter="{StaticResource BindingPathToValue}">
<Binding Mode="OneWay" Path="ObjPath" />
<Binding Mode="OneWay" Path="." />
</MultiBinding>
</TextBox.Text>
</TextBox>
</DataTemplate>

Resources