Bind checkbox visibility to converted bool and another checkboxes visibility - c#

Below binds the visibility of my checkbox to the converted bool. That works fine. How would i add a second condition? I only want to make the checkbox visible if the converted bool is true and another checkbox called Allowed is checked.
<CheckBox Grid.Row="3" Foreground="Black" Grid.ColumnSpan="2" x:Name="IsItComplete" IsThreeState="False"
BorderBrush="Black" VerticalContentAlignment="Center"
Checked="IsItComplete_Checked"
Style="{StaticResource CheckBoxStyle1}"
Visibility="{Binding Job.CanItBeComplete, Converter={StaticResource booleanToVisibilityConvertor},
Mode=OneWay,
Source={StaticResource Locator}}">
<CheckBox.Content>
<TextBlock Text="Is It Complete" Margin="0"/>
</CheckBox.Content>
</CheckBox>

If you are working under MVVM, one approach is to create a property in the ViewModel which will handle the logic and return a boolean indicating if the checkbox should be visible or not.
The second check box will have to be bound to a property as well, and be sure to do a TwoWay binding so that the property is updated when the checkbox is checked or not.
This should help: Bind two elements' Visibility to one property

This can be solved via two approaches i can think of over my head (ofcourse having single property in View model already suggested so i am not going to talk about it again)
Approach 1
Bind Visibility of checkBox with MultiValueConverter in place. Pass two bindings to it:
Actual bool property of class.
IsChecked DP of Allowed checkbox.
Converter will do AND operation on two values and will return accordingly.
<CheckBox>
<CheckBox.Visibility>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="IsEnable"/>
<Binding ElementName="Allowed" Path="IsChecked"/>
</MultiBinding>
</CheckBox.Visibility>
</CheckBox>
MutliValueConverter code:
public class MyConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
return ((bool)values[0] && (bool)values[1])
? Visibility.Visible : Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
Approach 2
Use MultiDataTrigger in checkBox style like this:
<CheckBox>
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnable}" Value="True"/>
<Condition Binding="{Binding ElementName=Allowed,
Path=IsChecked}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>

Related

DataTrigger on each item's textbox in ItemsControl

I have an ItemsControl that displays a list of Labels & TextBoxes that are used for data input and a button that executes some command when pressed (using the input values):
<DataTemplate x:Key="StringParameterTemplate">
<StackPanel Name="StackPanel_Parameter"
Orientation="Horizontal">
<Label Name="ParameterLabel"
Content="{Binding ParameterLabel}"
HorizontalContentAlignment="Right"
Width="200" />
<TextBox Name="ParameterTextBlock"
Text="{Binding ParameterValue, UpdateSourceTrigger=PropertyChanged}"
Width="300"/>
</StackPanel>
</DataTemplate>
. . .
<!-- Display all parameters -->
<ItemsControl Name="ListView_Parameters"
ItemsSource="{Binding ParameterCollection, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
ItemTemplateSelector="{StaticResource TaskParameterTemplateSelector}"
BorderThickness="0" />
<Button Name="ExecuteTaskButton"
Content="{Binding ButtonLabel}"
Style="{StaticResource ExecuteButtonStyle}"
Command="ExecuteTask">
I would like to create a style that enables/disables the button when ANY of the parameters from ListView_Parameters is empty. Something like this:
<!-- Execute button enable / disable -->
<Style x:Key="ExecuteButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Button.IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ListView_Parameters, Path=ParameterValue}" Value="">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
You can achieve this with a single binding using a converter.
<Button Content="{Binding ButtonLabel}"
IsEnabled="{Binding Path=ItemsSource,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}},
Converter={local:ItemsToBooleanConverter}}" />
Then your converter takes an input of the itemssource (for example a list of objects) - and can return true if all fields you want have values, false otherwise.
The converter is mostly boilerplate, but would look like something this:
public class ItemsToBooleanConverter : MarkupExtension, IValueConverter
... but the important part would like like this, if you were using a list:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var items = value as IList<ParameterList>;
return !(items.Any( <you check here for empty values> );
}
You'll need to be sure your parameter entry fields are bound properly to their sources so that the converter check is current.

How do I collapse an Expander whenever the DataGrid inside is null using XAML?

I have some code like this
<Expander IsExpanded="{Binding HasData}">
<DataGrid ItemsSource="{Binding SomeDataSource}">
<!-- some sode -->
</DataGrid>
</Expander>
Right now I am programmatically setting HasData to true whenever SomeDataSource is set to something not null.
Is it possible to achieve this using just XAML, without any c# code?
You could also use a DataTrigger and save yourself the converter.
<Expander>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<DataTrigger Binding="{Binding SomeDataSource, ConverterParameter=SomeData}" Value="{x:Null}">
<Setter Property="IsExpanded" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander>
One possible way is to use a converter to bind to SomeDataSource too:
public class IsNullConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter == null)
return value == null;
else
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
Then, you can use it in XAML only:
<Window.Resources>
<l:IsNullConverter x:Key="IsNullConverter"/>
</Window.Resources>
<Expander IsExpanded="{Binding SomeDataSource, Converter={StaticResource IsNullConverter}, ConverterParameter=SomeData}">
<DataGrid ItemsSource="{Binding SomeDataSource}">
<!-- some sode -->
</DataGrid>
</Expander>
EDIT
If it is no problem in your case, you can also bind to the DataGrid.HasItems property. It determines, if there are items in the source list. But it collapses the Expander too, when the source contains no items, but it is not null (source is an empty list).
<Expander IsExpanded="{Binding HasItems, ElementName=dg, Mode=OneWay}">
<DataGrid Name="dg" ItemsSource="{Binding SomeDataSource}">
<!-- some sode -->
</DataGrid>
</Expander>
Using a DataTrigger with an Expander Style and binding to the DataGrid itself works.
<Expander>
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="IsExpanded" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyDataGrid, Path=ItemsSource, UpdateSourceTrigger=PropertyChanged}" Value="{x:Null}">
<Setter Property="IsExpanded" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<DataGrid x:Name="MyDataGrid" ItemsSource="{Binding SomeDataSource, UpdateSourceTrigger=PropertyChanged}">
<!-- some code -->
</DataGrid>
</Expander>

c# / XAML highlight row in listbox that matches parent data

I have the following listbox -- the overall Datacontext goes to the class BatchRef
<ListBox x:Name="Details"
ItemsSource="{Binding BatchRef.ScheduleGroups}">
<ListBox.ItemTemplate>
<DataTemplate>
<localData:ScheduleGroupControl></localData:ScheduleGroupControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
BatchRef has an CurrentID field, and the ScheduleGroup Class has an ID field. I am looking for a way to determine if a ScheduleGroup ID field matches the CurrentID on the BatchRef field. One of the listbox items will always be a match to the parent, and I need to highlight the correct row.
You can use a DataTrigger to do this.
Something like this in your ItemTemplate:
<ListBox ItemsSource="{Binding Current.Groups}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CurrentId,
StringFormat={}CurrentID: {0}}"/>
<TextBlock Text="{Binding Path=DataContext.Current.CurrentId,
StringFormat={}ParentID: {0},
RelativeSource={RelativeSource AncestorType=Grid}}"/>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Background" Value="Red"/>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource IntEqualConverter}">
<Binding Path="CurrentId"/>
<Binding Path="DataContext.Current.CurrentId"
RelativeSource="{RelativeSource AncestorType=Grid}"/>
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter Property="Background" Value="Green"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I've used a IMultiValueConverter to check if the two values are equal:
public class IntEqualConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values.OfType<int>().Distinct().Count() == 1;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
This is a crude example. But it should point you in the right direction.
The important things to note are:
You need to use RelativeSource on the binding otherwise the DataContext will be that of the ListBoxItem, not your view's DataContext.
You will need to add the converter as a resource.
If it helps to understand. The Current property on my ViewModel is a BatchRef Object. The BatchRef class has a property called Groups which of type ObservableCollection<ScheduleGroup>.

Binding Button IsEnabled depending of the ComboBox selection

I'm sure this is really easy, but I don't know how do it.
I have a ComboBox and a Button, and I need to have the Button enabled only if the ComboBox has an item selected, i.e. if in the ComboBox has no elements showing, then the Button must be disabled. How can I do this?
I have tried doing the following:
IsEnabled="{Binding ElementName=mycombobox, Path=SelectedIndex}"/>
But it does not work. I'm using Silverlight 5.
Thanks in advance
MSDN has something that may be of help to you here. It suggests you use a converter or a data trigger. I haven't tested this, but perhaps this will work?
<Window.Resources>
<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedItem, ElementName=comboBox1}" Value="{x:Null}">
<Setter Property="UIElement.IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ComboBox Name="comboBox1">
<ComboBoxItem>One</ComboBoxItem>
<ComboBoxItem>Two</ComboBoxItem>
<ComboBoxItem>Three</ComboBoxItem>
</ComboBox>
<Button Style="{StaticResource MyButtonStyle}" Name="myButton" Content="Push me"/>
</Grid>
EDIT
I am under the impression that Cyndy has already figured all this out, but for any future readers...
As noted in the comments, you cannot do DataTriggers in Silverlight. You'll need to do a converter. Here is another post that may help. In essence, you'll need to have your XAML set something like:
<Button Content="MyButton" IsEnabled="{Binding SelectedItem, ElementName=comboBox1, Converter={StaticResource myConverter}}"/>
and then in your code-behind, you'll need something like:
public class MyConverter : 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)
{
throw new NotImplementedException();
}
}
There might be a more efficient way to do it, but I'd simply determine whether the ComboBox.SelectedItem is null in the SelectedIndexChanged event.

Is it possible to pass a parameter (a Binding) to a WPF Style

I have a WPF style that I am applying to list box items. Currently, each item is bound to a KeyValuePair. I display the Key in a TextBlock inside the ListBoxItem:
<TextBlock Name="Label" Text="{Binding Key}" />
What I want to do is make the Style generic so that if the data is not a KeyValuePair, (maybe just a string), I can bind the data correctly to the TextBlock.
Is there a way to pass a parameter to a Style or DataTemplate or make the data binding generic?
My style:
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Border" Padding="2" SnapsToDevicePixels="true" CornerRadius="4" BorderThickness="1">
<TextBlock Name="Label" Text="{Binding Key}" />
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="Border" Property="Background" Value="{StaticResource SelectionGradient}" />
</MultiTrigger>
<ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Let me give you a quick example. Let's say your TextBox contains a number, and you want it to be with a red Background if the number is negative, or with a green background if >= 0
Here is the style you'd write:
<TextBlock Text="{Binding Key}" >
<TextBlock.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="{Binding Key, Converter={StaticResource MyConverterResource}" />
</Style>
</TextBlock.Style>
</TextBlock>
Here is the converter you'd write:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double source = (double)value;
return source < 0 ? Brushes.Red : Brushes.Green;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// We don't care about this one here
throw new NotImplementedException();
}
}
And, to access the converter in your xaml, you just have to do the following, assuming your converter is in the namespace MyNamespace:
<Window xmlns:my="clr-namespace:MyNamespace">
<Window.Resources>
<my:MyConverter x:Key="MyConverterResource">
</Window.Resources?
<!-- Your XAML here -->
</Window>
(of course you can put this in any Resources, may it be a UserControl or whatever )
This will allow you to call your converter by writing {StaticResource MyConverterResource}
And here, you'd have a conditional style, the converter deciding which color to set as a background according to one parameter, in my case, the value itself (but it can be whatever you want)

Categories