MultiBinding Converter in CheckBox.IsChecked not called - c#

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.

Related

TextBlock MultiBinding throws exception

A TextBlock, should only be visible if a CheckBox is checked (x:Name=DisplaySubscriber) or the Binding string is not null or empty.
This is my approach:
<CheckBox Content="Display Subscriber" x:Name="DisplaySubscriber" />
<TextBlock Text="{Binding Path=SubscriberString, Mode=OneWay}" >
<TextBlock.Visibility>
<MultiBinding>
<Binding ElementName="DisplaySubscriber" Path="IsChecked" Mode="OneWay" Converter="{StaticResource BooleanToVisibleConverter}"/>
<Binding Path="SubscriberString" Mode="OneWay" Converter="{StaticResource StringIsNullEmptyConverter}"/>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
But I got this error:
Provide value on 'System.Windows.Data.MultiBinding' threw an
exception.
converter:
public class TextBlockVisibilityConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if((value[0] != null && (bool)value[0]) || (value[1]!=null && !String.IsNullOrEmpty(value[1].ToString())))
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
xaml:
define the converter in resource:
<local:TextBlockVisibilityConverter x:Key="TextBlockVisibilityConverter"/>
use it in the multibinding
<MultiBinding Converter="{StaticResource TextBlockVisibilityConverter}">
<Binding ElementName="DisplaySubscriber" Path="IsChecked"/>
<Binding Path="SubscriberString" />
</MultiBinding>

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;
}
}

Conditional text binding XAML

I have 3 properties that I'm trying to bind to a Textblock in XAML. One is a conditional and the other two are the strings that I want to display depending on that conditional.
<TextBlock Text="{Binding TrueText}" Style="{StaticResource styleSimpleText}" Visibility="{Binding ShowTrueText, Converter={StaticResource boolToVisibilityConverter}}"/>
<TextBlock Text="{Binding FalseText}" Style="{StaticResource styleSimpleText}" Visibility="{Binding ShowTrueText, Converter={StaticResource invertedBoolToVisibilityConverter}}"/>
This works, but now the textblocks have to have different names. Can I turn this into one TextBlock with the conditional inside of it?
You could achieve that with a Style and a DataTrigger:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding FalseText}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowTrueText}" Value="True">
<Setter Property="Text" Value="{Binding TrueText}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
An alternative would be to use a MultiBinding with a multi-value converter:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TextConverter}">
<Binding Path="TrueText"/>
<Binding Path="FalseText"/>
<Binding Path="ShowTrueText"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
The converter would look like this:
public class TextConverter : IMultiValueConverter
{
public object Convert(
object[] values, Type targetType, object parameter, CultureInfo culture)
{
var trueText = (string)values[0];
var falseText = (string)values[1];
var showTrueText = (bool)values[2];
return showTrueText ? trueText : falseText;
}
public object[] ConvertBack(
object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
The way we do this type of thing for MVVM is to create a property in your viewmodel for this. This allows for you to have unit testing for your condition on the viewmodel.
The property in your viewmodel will be the string value that the TextBlock is bound to. The viewmodel at some point will determine the value of that string based on the conditional logic that you need.
Yes, you can, just wrap them in a TextBlock as follows:
<TextBlock x:name="myTextBlock" Style="{StaticResource styleSimpleText}">
<TextBlock Text="{Binding TrueText}" Visibility="{Binding ShowTrueText, Converter={StaticResource boolToVisibilityConverter}}"/>
<TextBlock Text="{Binding FalseText}" Visibility="{Binding ShowTrueText, Converter={StaticResource invertedBoolToVisibilityConverter}}"/>
</TextBlock>
However, I think the best answer is the one provided by Clemens (using a DataTrigger).
You could set it up in your viewmodel and let it determine which text to show.
private static readonly string TRUETEXT = "This is the text to show when true";
private static readonly string FALSETEXT = "This is the text to show when false";
private bool _myBooleanProperty;
public bool MyBooleanProperty
{
get { return _myBooleanProperty; }
set
{
if (_myBooleanProperty != value)
{
_myBooleanProperty = value;
OnPropertyChanged("MyBooleanProperty");
OnPropertyChanged("ResultText");
}
}
}
public string ResultText
{
get
{
return MyBooleanProperty ? TRUETEXT : FALSETEXT;
}
}
Then you bind to it with just a single textblock. No visibility converter needed.
If there is a state where no text should show, you could work that in as well.
<TextBlock Text="{Binding ResultText}" Style="{StaticResource styleSimpleText}" />
In my opinion, the best solution to this problem would be a new string property in your view model which returns either TrueText or FalseText depending on the conditional. With such a property, you can just use a plain binding.
public string TheNewProperty
{
get
{
return ShowTrueText ? TrueText : FalseText;
}
}
<TextBlock Text="{Binding TheNewProperty}" Style="{StaticResource styleSimpleText}"/>

WPF ListView with group of RadioButtons and select default value

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

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