Binding Button IsEnabled depending of the ComboBox selection - c#

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.

Related

How to use XAML triggers to change TabControl Index when another tab disables?

What I have is a TabControl in a WPF application with two tabs. There is a collection called Voices, which you can see on line 6 as Path=GameMaster.Voices.Count. What I am trying to achieve is that when the collection is empty, the second tab disables, which I have done successfully. Then, I would like the TabControl's SelectedIndex to be set to 0. This then not only disables the second tab, but also forces the user back to the first tab. With the code below, the result works somewhat, however, when I add my first item to the collection, the tab index seems to set to -1.
Here is a screenshot of the TabControl upon application startup, when no objects are in the collection:
However, upon adding an item, the following occurs:
Here is my XAML code:
<TabControl Grid.Row="2" Margin="0,3,0,0">
<TabControl.Resources>
<Style TargetType="TabControl">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=GameMaster.Voices.Count, Converter={StaticResource CountGreaterThanZeroConverter}}"
Value="False">
<Setter Property="SelectedIndex" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
<TabItem Header="Add new voice">
<!-- TAB 1 CODE -->
</TabItem>
<TabItem Header="Edit Voices"
Name="EditTab"
IsEnabled="{Binding Path=GameMaster.Voices.Count, Converter={StaticResource CountGreaterThanZeroConverter}}">
<!-- TAB 2 CODE -->
</TabItem>
</TabControl>
And this is the Converter I am using:
internal class CountGreaterThanZeroConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var count = System.Convert.ToInt32(value);
return count > 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I have tried to understand what is going on, however I haven't found a suitable solution yet, and any help would be very much appreciated. Thank you.
Answering here, however, I am not 100% on why this has solved my issue. So, if someone could please let me know what the heck is going on, that would be a great asset to my knowledge!
I have added the following line of code:
<TabControl Grid.Row="2" Margin="0,3,0,0">
<TabControl.Resources>
<Style TargetType="{x:Type TabControl}">
<Setter Property="SelectedIndex" Value="0"/> <!-- This line -->
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=GameMaster.Voices.Count, Mode=OneWay, Converter={StaticResource CountGreaterThanZeroConverter}}"
Value="False">
<Setter Property="SelectedIndex" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
...
</TabControl>

Cannot Make Binding to ZIndex from Another Element in WPF

I want to make binding to the ZIndex of another element in WPF in the same .xaml file but it does not work.
The element to be bound.
<Border
x:Name="BubbleTop"
CornerRadius="5"
Background="#EBF5EB"
Padding="8,6"
BorderBrush="LightGray"
BorderThickness="1"
Grid.ZIndex="3">
<ContentPresenter />
</Border>
The element who initial a binding.
<TextBlock
x:Name="statusText"
Margin="..."
Foreground="{Binding ElementName=BubbleTop, Path=Grid.ZIndex, Converter={StaticResource ToggleColorConverter}}"
FontWeight="Bold"
Text="..."/>
In the converter, it is set to change the Foreground color according to the ZIndex of the Border element.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int z = (int)value;
if (z == 3)
return "Red";
else
return "Blue";
}
But it does not work. Any hint?
The converter you have would work fine, except that the Path for your binding is wrong. When binding to an attached property, you have to put the path in parens for the path to be parsed correctly.
That said, I don't think a converter really makes much sense here. You can use styling to address a simple toggle like this. This allows you to keep more of the view logic in XAML.
For example:
<TextBlock
x:Name="statusText"
Margin="..."
FontWeight="Bold"
Text="...">
<TextBlock.Style>
<p:Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"/>
<p:Style.Triggers>
<DataTrigger Binding="{Binding ElementName=BubbleTop, Path=(Grid.ZIndex)}" Value="3">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</p:Style.Triggers>
</p:Style>
</TextBlock.Style>
</TextBlock>
(Note: you can omit the p: XML namespace for the <Style/> element. I include that only because the Stack Overflow code formatter gets confused when there's a plain <Style/> element in XML and won't format the XML correctly.)
Try this in your value converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int z = (int)value;
if (z == 3)
return Brushes.Red;
else
return Brushes.Blue;
}

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>.

Automatic tooltip when text is trimmed in DataGrid

My application runs with C# and WPF (.net framework 4.0). My goal is to have a DataGrid in which the text in the cells is trimmed with an ellipsis, and automatically has a tooltip with the full text displayed only if the text in the cell is actually trimmed.
Solution 1: I'm currently using this to know if the text is trimmed or not: http://tranxcoder.wordpress.com/2008/10/12/customizing-lookful-wpf-controls-take-2/
The problem is that it only works when I resize the columns. The tooltips don't show up when the DataGrid is first loaded, when the columns are sorted, or when the ItemSource of the DataGrid is updated.
Solution 2: I've also tried this:http://www.scottlogic.com/blog/2011/01/31/automatically-showing-tooltips-on-a-trimmed-textblock-silverlight-wpf.html
But the tooltips never appear on my DataGrid cells, while it works fine with isolated textblocks.
I'm looking for simple ways to improve Solution 1 and make it work in my DataGrid in all cases, or maybe a different approach.
The style for Solution 1:
<UserControl.Resources>
<Style x:Key="TextColumnElementStyle" TargetType="TextBlock" BasedOn="{StaticResource TextBlockService}">
<Style.Setters>
<Setter Property="TextWrapping" Value="NoWrap" />
<Setter Property="TextTrimming" Value="WordEllipsis" />
</Style.Setters>
</Style>
</UserControl.Resources>
The source code of the TextBlockService
The DataGrid for Solution 1:
<DataGrid ItemsSource="{Binding IssueList}" tbs:TextBlockService.AutomaticToolTipEnabled="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Binding="{Binding Description}"
ElementStyle="{StaticResource TextColumnElementStyle}">
</DataGrid.Columns>
</DataGrid>
Thanks
I've found the perfect solution, based on an answer by xr280xr.
It works out of the box, in any condition, and without using additional code.
The style, that I put in <DataGrid.Resources> :
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}" TextTrimming="CharacterEllipsis">
<TextBlock.ToolTip>
<ToolTip Visibility="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget, Converter={StaticResource TrimToVisConverter}}">
<ToolTip.Content>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}"/>
</ToolTip.Content>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The source of Converter={StaticResource TrimToVisConverter}:
public class TrimmedTextBlockVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return Visibility.Collapsed;
FrameworkElement textBlock = (FrameworkElement)value;
textBlock.Measure(new System.Windows.Size(Double.PositiveInfinity, Double.PositiveInfinity));
if (((FrameworkElement)value).ActualWidth < ((FrameworkElement)value).DesiredSize.Width)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I can't comment yet, I lack points needed for it.
But I just want to add an extra tip to the accepted answer code, for noobs like me who want to use it.
If you want to link your Converter to its code, make sure to create a link in your Window/UserControl Resources as mentioned below:
<Window.Resources>
<local:TrimmedTextBlockVisibilityConverter x:Key="TrimToVisConverter" />
</Window.Resources>
Additional thought on accepted answer: You also have to rewrite any style that comes with default DataGridCell template as DataGridCell States. Otherwise you lose clean view on row select and other stuff...

Bind checkbox visibility to converted bool and another checkboxes visibility

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>

Categories