Enable/Disable controls on ComboBox selection in Xaml - wpf

How to enable /disable controls like textbox,label,textblock if combobox is selected/not-selected? e.g. If selected index is greater than zero, enable controls else disable.How to bind IsEnabled properties of the control with combobox selection?

You can bind IsEnabled to the SelectedIndex property of the ComboBox and use a IValueConverter to convert it to Boolean. For instance, in your XAML (showing enabling a Button):
<ComboBox x:Name="cmbBox" ItemsSource="{Binding Source={StaticResource DataList}}"/>
<Button Grid.Column="1" IsEnabled="{Binding ElementName=cmbBox, Path=SelectedIndex, Converter={StaticResource IndexToBoolConverter}}"/>
Then you need a converter as well, such as:
public class IndexToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((int)value > 0)
{
return true;
}
else
{
return false;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
You'll also have declare the Converter as a resource, such as in your Window.
<local:IndexToBoolConverter x:Key="IndexToBoolConverter"/>

I would probably just do something like this.
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedItem,
ElementName=TheCombo}"
Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ComboBox x:Name="TheCombo" Width="100">
<ComboBoxItem>Blah</ComboBoxItem>
<ComboBoxItem>Blah</ComboBoxItem>
<ComboBoxItem>Blah</ComboBoxItem>
</ComboBox>
<Button Content="Click Me" Margin="0,10"/>
</StackPanel>
</Grid>
Hope this helps, cheers!

Try with this
Dispatcher.BeginInvoke(new Action(() =>
{
ToggleButton dropDownButton = GetFirstChildOfType<ToggleButton>(cboMedicos);
if (dropDownButton != null)
{
dropDownButton.IsEnabled = false;
}
}), System.Windows.Threading.DispatcherPriority.Render);
public static T GetFirstChildOfType<T>(DependencyObject dependencyObject) where T : DependencyObject
{
if (dependencyObject == null)
{
return null;
}
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
{
var child = VisualTreeHelper.GetChild(dependencyObject, i);
var result = (child as T) ?? GetFirstChildOfType<T>(child);
if (result != null)
{
return result;
}
}
return null;
}

Related

How to write WPF DataTrigger within Border Style and how to Find Element in Setter?

I need a WPF DataTrigger for the Mouse Hover functionality of a Border. The Border Contains a Button, initially its Visibility is Collapsed. The Button should be Visible only on Mouse Hover otherwise Collapsed.
<Border Width="100" Height="30" HorizontalAlignment="Center" VerticalAlignment="Top" Background="#FFF2FFC6" Margin="0,20,0,0">
<Button x:Name="btn" Content="iApp" HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" Visibility="Collapsed" />
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True">
<Setter TargetName="btn" Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
Note: I need only DataTrigger. Don't suggest Event Trigger.
Here I Can't able to find the TargetName, it produces the Build Error "Error 1 The name "btn" is not recognized"
TragetName cannot be used in Style.Triggers. It should be used in ControlTemplete.Triggers.
You can write code like this(Not test).
Add the namespace in you xaml
xmlns:converter="clr-namespace:yours coverter's namespace"
Add the converter in your resources
<UserControl.Resources>
<converter:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
This is your border:
<Border x:Name="m_Border" Margin="0,20,0,0">
<Button x:Name="btn" Content="iApp" Visibility="{Binding IsMouseOver,ElementName=m_Border,Converter="{StaticResource BooleanToVisibilityConverter},ConverterParameter=Normal}"}" />
</Border>
Using this converter
public enum BooleanToVisibilityConverterType
{
/// <summary>
/// Normal
/// </summary>
Normal = 1,
/// <summary>
/// Reverse
/// </summary>
Reverse = 2
}
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var targertValue = false;
if (value == null)
{
throw new Exception("BooleanToVisibilityConverter - Convert Error");
}
else if (!Boolean.TryParse(value.ToString(), out targertValue))
{
throw new Exception("BooleanToVisibilityConverter - Convert Error");
}
else
{
var parameterValue = BooleanToVisibilityConverterType.Normal;
if (parameter != null)
{
Enum.TryParse<BooleanToVisibilityConverterType>(parameter.ToString(), out parameterValue);
}
if (parameterValue == BooleanToVisibilityConverterType.Reverse)
{
return targertValue ? Visibility.Collapsed : Visibility.Visible;
}
else
{
return targertValue ? Visibility.Visible : Visibility.Collapsed;
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var targetValue = Visibility.Collapsed;
if (value == null)
{
throw new Exception("BooleanToVisibilityConverter - ConvertBack Error");
}
else if (!Enum.TryParse<Visibility>(value.ToString(), out targetValue))
{
throw new Exception("BooleanToVisibilityConverter - ConvertBack Error");
}
else
{
var parameterValue = BooleanToVisibilityConverterType.Normal;
if (parameter != null)
{
Enum.TryParse<BooleanToVisibilityConverterType>(parameter.ToString(), out parameterValue);
}
if (parameterValue == BooleanToVisibilityConverterType.Reverse)
{
return targetValue == Visibility.Visible ? false : true;
}
else
{
return targetValue == Visibility.Visible ? true : false;
}
}
}
TargetName is mostly used within control templates and not simply within styles.
From MSDN:
You can set this property to the name of any element within the scope
of where the setter collection (the collection that this setter is
part of) is applied. This is typically a named element that is within
the template that contains this setter.
Also, to achieve your need, you should set trigger to Button not for Border
<Border x:Name="border" Width="100" Height="30" HorizontalAlignment="Center" VerticalAlignment="Top" Background="#FFF2FFC6" Margin="0,20,0,0">
<Button x:Name="btn" Content="iApp" HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" >
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=border, Path=IsMouseOver}" Value="false">
<Setter Property="Button.Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Border>

Create a usercontrol with overlapping content

I need to make a (user/custom) control which when 'folded open' displays content over other controls.
Like a popup, combo or menu except that the content will still open (think toggle button style).
Any leads?
Depends on many issues.
I know at least two ways to achive it. First - using <Popup />. Its easy to bind its IsOpen property to bool variable. And if you need to close it, I have got some smart behavior (which close popup when Esc key is pressed or when it lost focus).
The second way is to have a <ContentControl /> which have a style, which have a trigger, which changing ContentTemplate depending on what ever you want:
<ContentControl Grid.Row="3" Grid.ColumnSpan="2" Grid.RowSpan="2" Content="{Binding}">
<ContentControl.Style>
<Style>
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource OneTemplate}"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Property1}" Value="False"/>
<Condition Binding="{Binding Property2}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="ContentPresenter.ContentTemplate" Value="{StaticResource TwoTemplate}"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
I believe that there are exists many more possible ways to solve your problem!
Simplified description.
Main form
<Window>
<StackPanel Orientation="Vertical">
<ToolButton StackPanel.ZIndex="999"/>
<TextBlock>Other content</TextBlock>
</StackPanel>
</Window>
You'll need the ZIndex to indicate the overflowing control is on top
Custom control
<UserControl x:Class="ToolButton" Height="32">
<Canvas>
<ToggleButton x:Name="button">
<TextBlock>Content</TextBlock>
</ToggleButton>
<TextBlock Canvas.Top="32" Visibility="{Binding ElementName=button, Path=IsChecked, Converter={converter:BooleanToVisibility}}">Popup</TextBlock>
</Canvas>
</UserControl>
Bool to visibility Converter
public sealed class BooleanToVisibility : MarkupExtension, IValueConverter
{
public BooleanToVisibility() { }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var flag = false;
if (value is bool)
{
flag = (bool)value;
}
else if (value is bool?)
{
var nullable = (bool?)value;
flag = nullable.GetValueOrDefault();
}
if (parameter != null)
{
if (bool.Parse((string)parameter))
{
flag = !flag;
}
}
if (flag)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var back = ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
if (parameter != null)
{
if ((bool)parameter)
{
back = !back;
}
}
return back;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}

Converter never called when binding to Relative source

My Binding :
<StackPanel x:Name="Ancestor">
<StackPanel.Resources>
<converters:DiceInputToVisualConverter x:Key="MyDiceInputToVisualConverter" />
<Style TargetType="{x:Type Ellipse}">
<Setter Property="Visibility" Value="{Binding Path=/, Converter={StaticResource MyDiceInputToVisualConverter},FallbackValue=Visible}"></Setter>
</Style>
<StackPanel.Resources>
<StackPanel>
<Canvas DataContext="{Binding Path=DataContext.Dice1,RelativeSource={RelativeSource AncestorType=StackPanel}}">
<Ellipse Canvas.Left="5" Canvas.Top="5"></Ellipse>
<Ellipse Canvas.Left="5" Canvas.Top="20"></Ellipse>
</Canvas>
</StackPanel>
the DataContext :
Ancestor.DataContext = game ;
the converter :
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int dice = int.Parse(value.ToString());
if (dice == 3)
return Visibility.Visible;
return Visibility.Hidden;
}
my data source :
public Class Game : INotifyPropertyChanged
{
private int dice1;
public int Dice1
{
get { return dice1; }
set
{
dice1 = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Dice1"));
}
}
}
the binding is fine when i checked it with snoop the ellipse's DataContext had the desired value
but still the Converter is never called any ideas ?
Are you sure that you should be using Path=/? This notation means the currently selected item of the default collection view.
I see you have called a static resource
StaticResource MyDiceInputToVisualConverter
Where have you declared this resource. for example in the Window.Resources section Like the following.
<Window.Resources>
<!-- Converters that are used on the MainWindow. -->
<Converters:MyDiceInputToVisualConverter x:Key="MyDiceInputToVisualConverter" />
</Window.Resources>

binding radiobuttons group to a property in WPF

Let's imagine that I have:
<RadioButton GroupName="Group1" IsChecked="{Binding Path=RadioButton1IsChecked}" />
<RadioButton GroupName="Group1" IsChecked="{Binding Path=RadioButton2IsChecked}" />
And then in my data source class I have:
public bool RadioButton1IsChecked { get; set; }
public bool RadioButton2IsChecked { get; set; }
public enum RadioButtons { RadioButton1, RadioButton2, None }
public RadioButtons SelectedRadioButton
{
get
{
if (this.RadioButtonIsChecked)
return RadioButtons.RadioButton1;
else if (this.RadioButtonIsChecked)
return RadioButtons.RadioButton2;
else
return RadioButtons.None;
}
}
Can I somehow bind my radio buttons directly to SelectedRadioButton property? I really need RadioButton1IsChecked and RadioButton2IsChecked properties only to calculate the selected radiobutton.
Declare an enumeration similar to:
enum RadioOptions {Option1, Option2}
XAML:
<RadioButton IsChecked="{Binding SelectedOption, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:RadioOptions.Option1}}"/>
<RadioButton IsChecked="{Binding SelectedOption, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:RadioOptions.Option2}}"/>
Converter class:
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value) ? parameter : Binding.DoNothing;
}
}
<RadioButton GroupName="Group1" IsChecked="{Binding Path=SelectedRadioButton, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=RadioButton1}" />
<RadioButton GroupName="Group1" IsChecked="{Binding Path=SelectedRadioButton, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=RadioButton2}" />
public enum RadioButtons { RadioButton1, RadioButton2, None }
public RadioButtons SelectedRadioButton {get;set;}
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var ParameterString = parameter as string;
if (ParameterString == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object paramvalue = Enum.Parse(value.GetType(), ParameterString);
return paramvalue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var ParameterString = parameter as string;
var valueAsBool = (bool) value;
if (ParameterString == null || !valueAsBool)
{
try
{
return Enum.Parse(targetType, "0");
}
catch (Exception)
{
return DependencyProperty.UnsetValue;
}
}
return Enum.Parse(targetType, ParameterString);
}
}
We can create the radio buttons dynamically, ListBox can help us do that, without converters, quite simple.
The advantage is below:
if someday your enum class changes, you do not need to update the GUI (XAML file).
The steps are below:
create a ListBox and set the ItemsSource for the listbox as the enum and binding the SelectedItem of the ListBox to the Selected property.
Then the Radio Buttons for each ListBoxItem will be created.
public enum RadioButtons
{
RadioButton1,
RadioButton2,
None
}
Step 1: add the enum to static resources for your Window, UserControl or Grid etc.
<Window.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type system:Enum}"
x:Key="RadioButtons">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:RadioButtons" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Step 2: Use the List Box and Control Template to populate each item inside as Radio button
<ListBox ItemsSource="{Binding Source={StaticResource RadioButtons}}" SelectedItem="{Binding SelectedRadioButton, Mode=TwoWay}" >
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<RadioButton
Content="{TemplateBinding ContentPresenter.Content}"
IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource TemplatedParent},
Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
Now, enjoy~
References:
https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/
XAML:
<RadioButton IsChecked="{Binding Path=SelectedOption, UpdateSourceTrigger=PropertyChanged}">Option1</RadioButton>
<RadioButton IsChecked="{Binding Path=SelectedOption, UpdateSourceTrigger=PropertyChanged, Converter={v:NotBoolenConverter}}">Option2</RadioButton>
Converter:
public class NotBoolenConverter : IValueConverter
{
public NotBoolenConverter()
{
}
public override object Convert(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
bool output = (bool)value;
return !output;
}
public override object ConvertBack(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
bool output = (bool)value;
return !output;
}
}
Works with 2 radio buttons, by binding one to the opposite of the other.

adding conditional visibility to WPF control ToolTip

i would like to make a textblock tooltip conditionally visible.
i have the tooltip defined as:
<TextBlock>
<TextBlock.ToolTip>
<Grid>...</Grid>
</TextBlock.ToolTip>
</TextBlock>
where would visibility property go in that definition? it doesn't seem to like any of my guesses.
there are some suggestions of just working with grid visibility. the problem with that approach is making the grid invisible will still show empty tooltip box.. which is why i am trying to control tooltip visibility.
Try this. It won't leave an empty frame.
<TextBlock Text="test">
<TextBlock.ToolTip>
<ToolTip Visibility="Visible">
Hello
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
<TextBlock Text="test">
<TextBlock.ToolTip>
<ToolTip Visibility="Hidden">
Hello
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
The TextBlock with its ToolTip:
<TextBlock Text="{Binding Path=TextBoxText}">
<TextBlock.ToolTip>
<ToolTip Visibility="{Binding Path=ToolTipText, Converter={StaticResource StringToVisibilityConverter}}">
<Grid><TextBlock Text="{Binding Path=ToolTipText}" /></Grid>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
The object to are binding to the TextBlock:
public class TextBoxBindingObject
{
public string TextBoxText{ get; set; }
public string ToolTipText{ get; set; }
}
The converter:
[ValueConversion(typeof(string), typeof(Visibility))]
public class StringToVisibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if(value is string)
{
var stringValue = value as string;
if(!String.IsNullOrEmpty(stringValue))
return Visibility.Visible;
}
return Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Here you go;
<TextBlock Text="Dummy text">
<TextBlock.ToolTip>
<ToolTip Visibility="Collapsed">
<TextBlock Text="Text tooltip"></TextBlock>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
I realize this is a year old, but you can accomplish this in the code-behind.
private void control_ToolTipOpening(object sender, ToolTipEventArgs e)
{
if (condition)
e.Handled = true;
}
If you wanted to set a property here, you could do that, and bind it to the visibility. Don't forget to implement the INotifyPropertyChanged interface on your window.
Something like:
private void control_ToolTipOpening(object sender, ToolTipEventArgs e)
{
if (condition)
{
showControl=true;
e.Handled = true;
}
}
public Visibility showControl
{
get
{
return _showControl;
}
set
{
_showControl = value;
NotifyPropertyChanged("showControl");
}
}
and then bind it to the visibility property as
Visibility = "{Binding showControl}"
I'm typing this mainly to help anyone that comes across this from this point forward. I'm guessing you're not still stuck on this a year later, OP. =)
How about creating custom style for Tooltip ? That way you can re-use the same functionality at several places with minimal code.
Add this to a resourcedictionary and include it where ever you want to over-ride default tooltip behavior -
<Style TargetType="ToolTip" x:Key="{x:Type ToolTip}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content,
RelativeSource={RelativeSource Self},
Converter={local:ToolTipContentConverter}}"
Value="">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
Followed by the converter -
[ValueConversion(typeof(object), typeof(string))]
public class ToolTipContentConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value ?? string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
Hope this helps..
Amit
You should set the visibility on the grid :
<Window.Resources>
<BooleanToVisibilityConverter x:Key="visibilityConverter" />
</Window.Resources>
...
<Grid Visibility="{Binding IsToolTipVisible, Converter={StaticResource visibilityConverter}}>
...
</Grid>
If you don'e want the tooltip to show empty frame. You should create a separate tooltip ControlTemplate with all your required grid and textblocks and assign it to the tooltip template. This could help you solve the empty frame problem.
A much simpler solution than the other answers:
<TextBlock ToolTipService.IsEnabled="{Binding MakeToolTipVisible}">
<TextBlock.ToolTip>
<Grid>...</Grid>
</TextBlock.ToolTip>
</TextBlock>
where MakeToolTipVisible is your property.
Example: I want to store information about the line and add to my canvas
Line line = new Line();
line.X1 = 100;
line.Y1 = 100;
line.X2 = 500;
line.Y2 = 100;
line.Stroke = Brushes.Red;
line.StrokeThickness = 1;
line.ToolTip = new ToolTip { Content = "This is a line",Visibility= Visibility.Collapsed };
canvas.Children.Add(line);

Resources