Enable TextBox when ListViewItem is selected in WPF (databinding) - wpf

How can i enable/disable a TextBox with DataBinding in WPF when a ListViewItem is (not) selected?
I have created a converter class:
public class BoolConvert : 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 Exception("The method or operation is not implemented.");
}
}
and added the property to the TextBox:
IsEnabled="{Binding SelectedItem, ElementName=listViewCards, Converter={StaticResource BoolConvert}}"
but i have a XamlParseException becouse he can´t find the class :-(

You could alternately use a style trigger on the TextBox, eliminating the need for a ValueConverter:
<TextBox>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=lvItems, Path=SelectedItem}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<ListView Name="lvItems" .../>

You can bind the IsEnabled property on the TextBox to the SelectedItem property on the ListView. Then you'll need a converter (an implementation of IValueConverter) to convert selected values into boolean values.
<TextBox IsEnabled="{Binding SelectedItem, ElementName=listView, Converter={StaticResource MyConverter}}"/>
<ListView x:Name="listView" .../>
Then, in your converter:
public object Convert(object value, ...)
{
return value == null;
}

ListViewItem in ListView :
Enable if Selected.
Try the following:
<ListViewItem Margin="5" Background="AliceBlue">
<TextBox Margin="5" Text="Lösung SECHS"
IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}, Path=IsSelected}"/>
</ListViewItem>

Related

Binding a DataTrigger value to this instance of DataTemplate

I have a ListView, with its items represented by an ItemTemplate like so:
<ListView dependencyObjects:InterestingItem.Interesting="{Binding InterestingItem}"
ItemsSource="{Binding Quotations}" >
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<Grid>
<StackPanel x:Name="NotImportant">
</StackPanel>
<Grid x:Name="HiddenGrid"
Background="Red"
Visibility="Hidden" >
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<Grid.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.InterestingItem,
RelativeSource={RelativeSource AncestorType={x:Type ListView }}}"
Value="*this instance here*!">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Grid.Triggers>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The ListView has an attached property InterestingItem that is one of the items in the ListView.
What I can't hook up is when the InterestingItem is the same as one of the items, the second Grid should become visible.
I would prefer not to change and bind to the actual objects in the list - but rather have the ListView control which item is to be altered.
What is the Value in the DataTrigger that I need?
There are multiple issues in your XAML and conceptually that prevent it from working.
To bind attached properties, you have to use the correct syntax with parentheses.
Path="{Binding (local:InterestingItem.Interesting), RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
See the Binding path syntax documenation for reference.
The Triggers property does only support EventTriggers, see FrameworkElement.Triggers.
Note that the collection of triggers established on an element only supports EventTrigger, not property triggers (Trigger). If you require property triggers, you must place these within a style or template and then assign that style or template to the element either directly through the Style property, or indirectly through an implicit style reference.
You cannot bind the Value property of DataTrigger, since it is not a dependency property.
You could of course change the bound type to expose a property that indicates if it is a special object or not and bind that in XAML using a DataTrigger, similar to this (where IsSpecial is the new bool property).
<Grid x:Name="HiddenGrid"
Background="Red">
<TextBlock Text="Hidden Grid"/>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSpecial}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
If you want to stick to your current approach, you could create a custom IMultiValueConverter that enables binding multiple properties. It would check if all of the bound values are equal and return Visibility.Visible or Visibility.Hidden otherwise. This example uses Linq to check this and supports an arbitrary number of values bound, but there are many other options.
public class EqualityToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values is null || values.Length < 2)
return Binding.DoNothing;
return values.Distinct().Count() == 1 ? Visibility.Visible : Visibility.Hidden;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
Next, instantiate the converter in the resources of the ListView or any other resource dictionary in scope and bind the Visibility property of the Grid to both the current item (just <Binding/>) and the attached property local:InterestingItem.Interesting with a MultiBinding that uses the converter to convert them to a Visibility.
<ListView local:InterestingItem.Interesting="{Binding InterestingItem}"
ItemsSource="{Binding Quotations}">
<ListView.Resources>
<local:EqualityToVisibilityConverter x:Key="EqualityToVisibilityConverter"/>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<Grid>
<StackPanel x:Name="NotImportant">
<TextBlock Text="Not Important"/>
</StackPanel>
<Grid x:Name="HiddenGrid"
Background="Red">
<Grid.Visibility>
<MultiBinding Converter="{StaticResource EqualityToVisibilityConverter}">
<Binding/>
<Binding Path="(local:InterestingItem.Interesting)"
RelativeSource="{RelativeSource AncestorType={x:Type ListView}}"/>
</MultiBinding>
</Grid.Visibility>
<TextBlock Text="Hidden Grid"/>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
There are two other things to note here. I have added two dummy TextBlocks, otherwise the result will not be visible, as the panels are empty. Replace them with your content. Furthermore, both the StackPanel and the Grid are overlapping in the parent Grid, I do not know if this is intentional or not, but you can change it by adding rows or columns and moving the elements there.
What is the Value in the DataTrigger that I need?
I am afraid XAML has no support for something like the this keyword in C#.
You may use a MultiBinding with an IMultiValueConverter implementation that determines whether the items are equal:
<Grid x:Name="HiddenGrid" Background="Red">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding>
<MultiBinding.Converter>
<local:MultiConverter />
</MultiBinding.Converter>
<Binding Path="{Binding Path=DataContext.InterestingItem,
RelativeSource={RelativeSource AncestorType={x:Type ListView }}}" />
<Binding Path="{Binding}" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
Converter:
public class MultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
values != null && values.Length == 2 && values[0] == values[1];
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
}
Note that you cannot set the Visibility property of the Grid to a local value if you want to be able to override the value using a Style setter.
<Grid x:Name="HiddenGrid" Background="Red" Visibility="Hidden">

Enable/Disable Combobox if label content changed

Is it possible to enable/disable the combobox if the label has content in the xaml? (I am looking for a xaml solution.)
<Label x:Name="lbl_AusgewählteEmail" HorizontalAlignment="Left"
Margin="37,132,0,0" VerticalAlignment="Top" Width="607"
Content="{Binding ElementName=combx_UnzustellbarMailAuswahl, Path=SelectedItem}"/>
<ComboBox x:Name="combx_Auswahl" HorizontalAlignment="Left"
Margin="37,219,0,0" VerticalAlignment="Top" Width="318"/>
In pure XAML, no. You can however use an IValueConverter to turn that string into a boolean:
public class NonEmptyStringToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string)
return !String.IsNullOrEmpty((string) value);
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
<Window.Resources>
<yourNameSpace:NonEmptyStringToBooleanConverter x:Key="StringToBool"/>
</Window.Resources>
<Label x:Name="lbl_AusgewählteEmail" HorizontalAlignment="Left"
Margin="37,132,0,0" VerticalAlignment="Top" Width="607"
Content="{Binding ElementName=combx_UnzustellbarMailAuswahl, Path=SelectedItem}"/>
<ComboBox x:Name="combx_Auswahl" HorizontalAlignment="Left"
Margin="37,219,0,0" VerticalAlignment="Top" Width="318"
IsEnabled="{Binding ElementName=combx_UnzustellbarMailAuswahl, Path=SelectedItem, Converter={StaticResource StringToBool}"/>
You could potentially also do this via a Style but that would be a bit weird to be honest. For the sake of completeness:
Include the following namespace at the top of your containing control/window:
xmlns:system="clr-namespace:System;assembly=mscorlib"
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=combx_UnzustellbarMailAuswahl, Path=SelectedItem}" Value="{x:Static system:String.Empty}">
<Setter Property="IsEnabled" Value="False"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=combx_UnzustellbarMailAuswahl, Path=SelectedItem}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>

how to disable a button when a textbox becomes readonly?

I have a button and text box inside a stackpanel, when text box gets readonly state then the button should disable, i want to do this in XAML only is there any way to do this?
The binding is easy to define but you need a converter to invert the boolean value. Since you want to enable (IsEnabled = true) the Button when the TextBox is not readonly (IsReadOnly = false).
XAML
<StackPanel>
<StackPanel.Resources>
<local:InvertBooleanConverter x:Name="invertBoolConverter"/>
</StackPanel.Resources>
<TextBox x:Name=textBox />
<Button IsEnabled={Binding IsReadOnly, ElementName=textBox, Converter={StaticResource invertBoolConverter}}/>
</StackPanel>
local is a namespace you define in your UserControl that points to the namespace of the InvertBooleanConverter
xmlns:local="clr-namespace:NamespaceOfTheInvertBooleanConverter"
Converter
public class InvertBooleanConverter: IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
bool currentValue = System.Convert.ToBoolean(value);
return !currentValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
bool currentValue = System.Convert.ToBoolean(value);
return !currentValue;
}
}
<StackPanel>
<TextBox x:Name="targetTB">Hello</TextBox>
<Button Content="Test Button">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=targetTB, Path=IsReadOnly}" Value="True">
<Setter Property="Button.IsEnabled" Value="False"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=targetTB, Path=IsReadOnly}" Value="False">
<Setter Property="Button.IsEnabled" Value="True"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
try this, it can work as you need.

WPF Progress bar

WPF: I have a Problem in Progressbar i want it to show when the operation is not finished and when my operation is finished it will hide. Please show me understandable example so i can apply it to my work. Thanks in advance!
you can do that in different scenarios.
using triggers, (I'd prefer that)
<ProgressBar Maximum="100" Margin="10,107,232,168" Value="0" Name="progr">
<ProgressBar.Resources>
<Style TargetType="{x:Type ProgressBar}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Value}" Value="100">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Resources>
</ProgressBar>
Using converters
<Grid>
<Grid.Resources>
<delWpf:VisibilityConverter x:Key="conv"/>
</Grid.Resources>
<ProgressBar Name="prog2" Minimum="0" Maximum="100"
Value="{Binding CurrentIndex, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=Value, Mode=OneWay, Converter={StaticResource conv}}" />
</Grid>
and converter
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Math.Abs((double)value - 100) < 0.001 ? Visibility.Hidden : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You might use Extended WPF Toolkit which has a BusyIndicator control,
http://wpftoolkit.codeplex.com/wikipage?title=BusyIndicator
Samples are included in the download.
For your information, Microsoft introduces BusyIndicator in Silverlight first (but fails to ship one for WPF) as a replacement of progress bar.

AutoHide Progressbar with StyleTriggers

I would like to hide a progressbar in WPF using databinding. Whenever a property is 0, the progressbar should hide: I try the following code
(Info: My current datacontext is a class that holds an integer property 'CurrentIndex')
<ProgressBar Minimum="0" Maximum="100" Value="{Binding CurrentIndex, UpdateSourceTrigger=PropertyChanged}" Visibility="Visible">
<ProgressBar.Style>
<Style TargetType="{x:Type ProgressBar}">
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentIndex}" Value="0">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Style>
</ProgressBar>
What is wrong with this code? Why does the progressbar still show up when the CurrentIndex is 0? (in the model behind, the value of 'CurrentIndex' is 0 by default, when the control is loaded)
DP precedence, do not set Visibility on the control itself (local value > style).
Other way to use visibility binding and a converter:
<Grid>
<Grid.Resources>
<App:VisibilityConverter x:Key="VisibilityConverter" />
</Grid.Resources>
<ProgressBar Minimum="0" Maximum="100" Value="{Binding CurrentIndex, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding CurrentIndex, Mode=OneWay, Converter={StaticResource VisibilityConverter}}" />
</Grid>
The converter code (VisibilityConverter.cs):
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value == 0 ? Visibility.Hidden : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Your XAML was almost right!
Define your progressbar as you did:
<ProgressBar Minimum="0"
Maximum="100"
Value="{Binding CurrentIndex, UpdateSourceTrigger=PropertyChanged}"
Name="MyAutoHidingProgressBar" />
Don't forget to add the Name property AND do not set the Visibility here.
It will always override what is set in your Style.
Then define a Style as normal in your <Window.Resources>
<Window.Resources>
<Style TargetType="ProgressBar" x:Key="MyAutoHidingProgressBarStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyAutoHidingProgressBar, Path=Value}" Value="0">
<Setter Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
What this is basically doing is to check the Value of the progressbar itself, rather than your binding.
As a last step add the Style to your progressbar:
Style="{StaticResource MyAutoHidingProgressBarStyle}"
Now your ProgressBar will auto hide if its Value is 0.
You can also easily add a Trigger to hide it if its full.

Resources