Just wondering if it is possible to show a WPF on a disabled item ONLY (and not when the item is enabled).
I would like to give the user a tooltip explaining why an item is currently disabled.
I have an IValueConverter to invert the boolean IsEnabled property binding. But it doesn't seem to work in this situation. The ToolTip is show both when the item is enabled and disabled.
So is is possible to bind a ToolTip.IsEnabled property exclusively to an item's own !IsEnabled?
Pretty straightforward question I guess, but code example here anyway:
public class BoolToOppositeBoolConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
#endregion
}
And the binding:
<TabItem Header="Tab 2" Name="tabItem2" ToolTip="Not enabled in this situation." ToolTipService.ShowOnDisabled="True" ToolTipService.IsEnabled="{Binding Path=IsEnabled, ElementName=tabItem2, Converter={StaticResource oppositeConverter}}">
<Label Content="Item content goes here" />
</TabItem>
Thanks folks.
JustABill's suggestion worked. I also needed to define the string as a resource to avoid problems with quotation marks. And you still need to set ToolTipService.ShowOnDisabled="True".
So, here is the working code which shows how to display a tooltip in WPF only when an item is disabled.
In the top container, include the system namespace (see sys below). I also have a Resources namespace, which I called "Res".
<Window x:Class="MyProjectName.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Res="clr-namespace:MyProjectName.Resources"
>
Then you need
<Window.Resources>
<Res:FalseToStringConverter x:Key="falseToStringConv" />
<sys:String x:Key="stringToShowInTooltip">This item is disabled because...</sys:String>
</Window.Resources>
In my case, it was a tab item that I was interested in. It could be any UI element though...
<TabItem Name="tabItem2" ToolTipService.ShowOnDisabled="True" ToolTip="{Binding Path=IsEnabled, ElementName=tabItem2, Converter={StaticResource falseToStringConv}, ConverterParameter={StaticResource stringToShowInTooltip}}">
<Label Content="A label in the tab" />
</TabItem>
And the converter in code behind (or wherever you want to put it). Note, mine went into the a namespace called Resources, which was declared earlier.
public class FalseToStringConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool && parameter is string)
{
if ((bool)value == false)
return parameter.ToString();
else return null;
}
else
throw new InvalidOperationException("The value must be a boolean and parameter must be a string");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
A little out of date, but I got this working by setting RelativeSource mode to Self instead of setting the ElementName within the Binding.
<TabItem Header="Tab 2" Name="tabItem2" ToolTip="Not enabled in this situation." ToolTipService.ShowOnDisabled="True" ToolTipService.IsEnabled="{Binding Path=IsEnabled, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource oppositeConverter}}">
<Label Content="Item content goes here" />
</TabItem>
Related
<Button
VerticalAlignment="Stretch"
ToolTip="Convert selected Node(s)"
ToolTipService.ShowOnDisabled="False"
Visibility="{Binding Source={StaticResource serviceLocator}, Path=NetworkManager.NetworkViewModel.IsGenericProfileLoaded}"
Command="{Binding Path=ConvertToNonControlPlaneCommand}" >
<Image
Style="{StaticResource toolbarImageStyle}"
Source="/Resources/Icons/equipment_edit.png"
/>
</Button>
The button should be collapsed or invisible depending on the Path property called IsGenericProfileLoaded. Even if IsGenericProfileLoaded is false, the button is still visible. How to make it invisible. From other question on stackoverflow, it seems that i need to visibilitytoBoolean converter or is there anything that I can use here like disable="true"?
In general when a data binding does not work, debug the app in Visual Studio and look in the debug output. You can show the debug output by: menu / DEBUG / Windows / Output. Go in the application to that page and keep an eye to the messages shown in the debug output window. You will get some information about what does not work.
You are correct, in your case you need a converter. The data binding alone will not be able to assign a bool to a dependency property of type Visibility.
Here is a very simple version of such convertor:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool booleanValue = (value as bool?).GetValueOrDefault();
return booleanValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
You will have to also add somewhere in XAML a static reference that makes this convertor available. Maybe in App.xaml something like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:<replace_with_your_namespace>;assembly=<replace_with_your_assembly>">
...
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
...
</ResourceDictionary>
In your case the visibility will have to be set like this:
Visibility="{Binding Source={StaticResource serviceLocator}, Path=NetworkManager.NetworkViewModel.IsGenericProfileLoaded, Converter={StaticResource BoolToVisibilityConverter}}"
You will soon see that you need more flexibility from the convertor. You will want to convert a true to Visibility.Collapsed and a false to Visibility.Visible. Here is how such a more flexible convertor would be used in XAML:
Visibility="{Binding IsLocked, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=not}"
And here is how the convertor method would have to be rewritten:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool booleanValue = (value as bool?).GetValueOrDefault();
if (parameter != null)
{
if (parameter.ToString().Equals("not", StringComparison.OrdinalIgnoreCase))
{
booleanValue = !booleanValue;
}
else
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid value for the BoolToVisibilityConverter parameter: '{0}'. The only valid values are null or 'not' (case insensitive)", parameter));
}
}
return booleanValue ? Visibility.Visible : Visibility.Collapsed;
}
How can I bind an inverted value of a bool in my xaml?
I know, I know, there are a lot of q&a about this, but there aren't any about a specific case: when the converter is in a view model and the radio button in a user control.
how can I correctly refer to the main window view model data context? In other case I used the code I've posted, but in this case I don't get how to use it.
UPDATE CODE:
namespace ***.ViewModel
{
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
class MainWindowViewModel : MyClass.UtilityClasses.ViewModelBase
{
public MainWindowViewModel(){
SaveCommand = new DelegateCommand(SaveData, CanSave );
UndoCommand = new DelegateCommand(UndoActions, CanSave);
[...]
}
....
}
ANOTHER CODE UPDATE:
In my xaml, I have a devexpress GridControl where I list my observableCollection from db. When I click one element, a layout group is populated with the relative data.
In this layout group, I have:
<StackPanel Margin="0" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<RadioButton Content="{DynamicResource Locale}" Margin="10,0,0,0" x:Name="rd_LOCALE" VerticalAlignment="Center" IsChecked="{Binding Path=REMOTO, Converter={StaticResource InverseBooleanConverter}}" GroupName="Location" Panel.ZIndex="9" TabIndex="10" />
<RadioButton Content="{DynamicResource Remoto}" Margin="10,0,6,0" x:Name="rd_REMOTO" VerticalAlignment="Center" IsChecked="{Binding REMOTO}" GroupName="Location" Panel.ZIndex="10" TabIndex="11" Tag="PRISMA" />
</StackPanel>
WhenI change from one record to another, the converter gives me error... why? It fails the "if (targetType != typeof(bool))" row. So I tried to change that like:
if (value.getType() != typeof(bool))
but in this way, without fails nothing in the code, it put the wrong value in the radiobutton!
You need to make sure that the value converter is referenced in the XAML
<UserControl.Resources>
<myproject:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources>
where my project is the namespace in which the value converter class is defined
My localized resource string, named TextResource has the value: Text: {0}. Where {0} is the placeholder for String.Format.
My user control has a DependecyProperty called Count.
I would like to bind Count to the text of a text box but also apply the localized string. So that the content of the text block is Text: 5 (assuming the value of Count is 5)
I managed to figure out how to bind the localized string
<TextBlock Text="{Binding Path=LocalizedResources.TextResource, Source={StaticResource LocalizedStrings}}" />
or the property value
<TextBlock Text="{Binding Path=Count}" />
but not both simultaneous.
How can I do that in XAML?
PS: One option would be to add two text blocks instead of one but I am not sure if that is a good practice.
You have three options here.
First option: Modify your view model to expose your formatted string and bind to that.
public string CountFormatted {
get {
return String.Format(AppResources.TextResource, Count);
}
}
<TextBlock Text="{Binding Path=CountFormatted}" />
Second option: Make a converter MyCountConverter
public class MyCountConverter: IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value == null)
return value;
return String.Format(culture, AppResources.TextResource, value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
<phone:PhoneApplicationPage.Resources>
<local:MyCountConverter x:Key="MyCountConverter"/>
</phone:PhoneApplicationPage.Resources>
...
<TextBlock Text="{Binding Count, Converter={StaticResource MyCountConverter}}"/>
Third option: Use bind-able converter parameter so that you can make a general StringFormat converter where you can actually bind the converter parameter. This is not supported out of the box in windows phone but is still doable. Check this link on how it can be done.
However, unless you are using resources to support multiple languages then it's much easier to just pass your format as a plain string to a converter.
<TextBlock Text="{Binding Count,
Converter={StaticResource StringFormatConverter},
ConverterParameter='Text: {0}'}" />
You'll have to make a StringFormatConverter converter that uses the parameter in this case.
Edit:
Regarding third option, you can use the IMultiValueConverter in the link above to achieve what you want. You can add the following converter:
public class StringFormatConverter: IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
var param = values[0].ToString();
var format = values[1].ToString();
return String.Format(culture, format, param);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
<TextBlock Text="{Binding ElementName=MultiBinder, Path=Output}" />
<binding:MultiBinding x:Name="MultiBinder" Converter="{StaticResource StringFormatConverter}"
NumberOfInputs="2"
Input1="{Binding Path=Count, Mode=TwoWay}"
Input2="{Binding Path=LocalizedResources.TextResource, Source={StaticResource LocalizedStrings}}" />
I don't know if it's worth the effort though.
I am using WPF.
I want to show button when the label is not empty. When Label has a value, the button will be hidden.
How can I do this with WPF? Using <Style>?
Code:
<Label Name="lblCustomerName"/>
<Button Name="btnCustomer" Content="X" Visibility="Hidden" />
try
if (string.IsNullOrEmpty(lblCustomerName.Text)) {
btnCustomer.Visibility = Visibility.Hidden;
}
else {
btnCustomer.Visibility = Visibility.Visible;
}
You will need to use a converter and bind it to the content of lblCustomer.
public class ContentNullToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return Visibility.Hidden;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
More on converters here.
Then in xaml you can do the following:
First line needs to be defined in your resources where you will need to qualify it with the namespace in which you created the class above. Once you have defined the resource you can use the second part.
<ContentNullToVisibilityConverter x:Key="visConverter"/>
<Label Name="lblCustomerName"/>
<Button Name="btnCustomer" Content="X" Visibility="{Binding ElementName=lblCustomer, Path=Content, Converter={StaticResource visConverter}}" />
I have a property of datattype enum : like
public BreakLevel Level
{
get { return level; }
set { level = value; }
}
And enum defined :
public enum BreakLevel
{
Warning, Fatal
}
I want bind the neum property to the visibility of my border , somewhat like this:
Visibility="{Binding BreakLevel.Fatal}"
so is it possible?
<Border CornerRadius="4" BorderThickness="1" BorderBrush="#DAE0E5"
Visibility="{Binding DataContext.IsError, Converter={StaticResource BoolToVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}" >
Scott has a good answer to the actual question, however its always a good idea to ask yourself "How might I need code like this in the future? How can I avoid creating yet another class and instead re-use what I have already got?".
Here is a more general variation of Scott's solution:-
public class EnumToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (Enum.GetName(value.GetType(), value).Equals(parameter))
return Visibility.Visible;
else
return Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Xaml:-
<TopLevelWindowOrControl.Resources>
<local:EnumToVisibilityConverter x:Key="EnumToVisibilityConverter" />
</TopLevelWindowOrControl.Resources>
<Border Visibility="{Binding Path=BreakLvlProperty, Converter={StaticResource EnumToVisibilityConverter}, ConverterParameter=Fatal" />
In this approach we can converter any enum to a Visibility value by using the ConverterParameter to specify the enum value (in string form) that constitute the "Visible" state.
Its tempting to take this further to allow more than one enum value to be equated to "Visible". However currently the code isn't much more complicated than Scott's more specific implementation. Hence this enhancement should be left until needed.
I think you can just create a BreakLevelToVisibilityConverter and bind just like the example you provided.
I'm assuming that the DataContext of your border is set to an instance of a class that has a property of type 'BreakLevel' (we'll call this property 'BreakLvlProperty').
The code below will then show the border if the value of BreakLvlProperty is BreakLevel.Fatal
Converter:
public class BreakLevelToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((BreakLevel)value == BreakLevel.Fatal)
return Visibility.Visible;
else
return Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
XAML:
<TopLevelWindowOrControl.Resources>
<local:BreakLevelToVisibilityConverter x:Key="BreakLevelToVisibilityConverter" />
</TopLevelWindowOrControl.Resources>
<Border Visibility="{Binding Path=BreakLvlProperty, Converter={StaticResource BreakLevelToVisibilityConverter}" />
public class EnumToVisibilityConvertor : IValueConverter
{
private bool chk;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((value != null) && (value is BreakLevel) && (targetType == typeof(Visibility)))
{
chk = ((((BreakLevel) value) == (BreakLevel) Enum.Parse(typeof (BreakLevel), parameter.ToString(), true)));
return (chk==true) ? Visibility.Visible : Visibility.Collapsed;
}
throw new InvalidOperationException("Invalid converter usage.");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
<Border CornerRadius="4" BorderThickness="1" BorderBrush="#DAE0E5"
Visibility="{Binding Path=Level, Converter={StaticResource enumToVisibilityConvertor},ConverterParameter=Fatal}" >
I know it's a bit old question but all of you focused on converters and there's much better (in my opinion) way of doing it without involving code-behind:
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<Grid>
<!-- Here is the border which will be shown if Object.Level has value Fatal -->
<Border x:Name="PART_Border"
Visibility="Collapsed"
BorderThickness="3" BorderBrush="Red"
CornerRadius="4">
</Border>
<TextBlock>Interiors of the border</TextBlock>
</Grid>
<ControlTemplate.Triggers>
<!-- This is the code which turns on border's visibility -->
<DataTrigger Binding="{Binding Level}" Value="{x:Static local:BreakLevel.Fatal}">
<Setter TargetName="PART_Border" Property="Visibility" Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
I assumed that in DataContext resists an object which has a property Level which is of your BreakLevel type.