Button visibility after enter value (XAML, C#, IValueConverter) - c#

I have xaml file where I put 2 TextBox where I can put login and password. After then Button "Login" should be visible. Here is my code:
TreeView.xaml file:
<UserControl x:Class="LayoutMVVM.Views.TreeView"
xmlns:local="clr-namespace:LayoutMVVM.Views"
xmlns:Treemodels="clr-namespace:LayoutMVVM.ViewModels" .../>
<UserControl.Resources>
<Treemodels:VBConverter x:Key="VBConverter" />
</UserControl.Resources>
<Grid>
....
<TextBox Grid.Row="2" Grid.Column="2" Text="{Binding Login, UpdateSourceTrigger=PropertyChanged}" Background="AliceBlue" />
<TextBox Grid.Row="1" Grid.Column="2" Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Background="AliceBlue" />
<Button Grid.Row="3" Grid.Column="3" Visibility="{Binding IsVerifyTrue, Converter={StaticResource VBConverter}}" Content="Login" />
</Grid>
TreeView.xaml.cs
namespace LayoutMVVM.Views
{
public partial class TreeView : UserControl
{
public TreeView()
{
InitializeComponent();
}
public TreeView _isVerifyTrue;
public TreeView IsVerifyTrue
{
get { return this; }
set
{
_isVerifyTrue = value;
OnPropertyChanged(() => IsVerifyTrue);
}
}
private void OnPropertyChanged(Func<object> p)
{
throw new NotImplementedException();
}
private string _login;
public string Login
{
get { return _login; }
set
{
_login = value;
IsVerifyTrue = this;
OnPropertyChanged(() => Login);
}
}
private string _password;
public string Password
{
get { return _password; }
set
{
_password = value;
IsVerifyTrue = this;
OnPropertyChanged(() => Password);
}
}
}
and seperate class for VBConverter.cs:
namespace LayoutMVVM.ViewModels
{
public class VBConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is TreeView)
{
var tv = value as TreeView;
bool result = !string.IsNullOrEmpty(tv.Login)
&& !string.IsNullOrEmpty(tv.Password);
return result? Visibility.Visible : Visibility.Hidden;
}
return Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
When I start app button is always visible and I'm not know what is wrong.

You'll need a MultiValueConverter for this. See below example :
XAML :
<Window x:Class="SOSample1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SOSample1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<local:VisibilityConverter x:Key="VisibilityConverter" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Name="user" Height="100" Width="200" Text="{Binding Login, UpdateSourceTrigger=PropertyChanged}" Background="AliceBlue" />
<TextBox Grid.Row="1" Name="password" Height="100" Width="200" Text="{Binding Login, UpdateSourceTrigger=PropertyChanged}" Background="AliceBlue" />
<Button Grid.Row="2" Content="Login" Height="100" Width="200" >
<Button.Visibility>
<MultiBinding Converter="{StaticResource VisibilityConverter}" >
<Binding ElementName="user" Path="Text" />
<Binding ElementName="password" Path="Text" />
</MultiBinding>
</Button.Visibility>
</Button>
</Grid>
Converter :
public class VisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool result = false;
if (values != null)
{
foreach (var item in values)
{
result = (item as string).Length > 0;
if (!result) break;
}
}
return (Visibility)new BooleanToVisibilityConverter().Convert(result, targetType, parameter, culture);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
OUTPUT

Related

WPF converting and binding checkbox value to visibility of UI element

Im trying to create sample project for testing when checkbox is checked show textbox, otherwise hide it in WPF. So, i created following ViewModel:
internal class TestViewModel
{
public bool IsCheckBoxChecked { get; set; }
}
and "boolean to visibility" converter helper calss:
public class MyBoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool && (bool)value)
{
return Visibility.Hidden;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility && (Visibility)value == Visibility.Visible)
{
return true;
}
return false;
}
}
My XAML source:
<Window x:Class="MyWpfAppSample1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyWpfAppSample1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterOwner">
<Window.Resources>
<local:MyBoolToVisibilityConverter x:Key="BoolToVisibility"/>
</Window.Resources>
<Grid>
<CheckBox x:Name="chBox" Content="Is TextBox visible?" HorizontalAlignment="Left" Margin="39,37,0,0" VerticalAlignment="Top" FontSize="16" IsChecked="{Binding IsCheckBoxChecked}"/>
<TextBox HorizontalAlignment="Left" Height="38" Margin="63,98,0,0" TextWrapping="Wrap" Text="Test text" VerticalAlignment="Top" Width="476" FontSize="14" Visibility="{Binding Path=IsCheckBoxChecked, Converter={StaticResource BoolToVisibility}}"/>
</Grid>
</Window>
and my XAML form source:
public partial class MainWindow : Window
{
private TestViewModel viewModel;
public MainWindow()
{
InitializeComponent();
viewModel = new TestViewModel();
chBox.DataContext = viewModel;
}
}
but it is not working as i expected

WPF Using ElementName for visibility issue

I have an issue with setting the visibility of a control when specifying the context using ElementName.
For some reason wpf seems to behave differently when using ElementName, versus using the context that is set from the code-behind.
The following code works fine. The collection contains two elements, and the button is set to visible.
MainWindow.xaml
<Window x:Class="WpfObservableCollectionVisibility.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfObservableCollectionVisibility"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" x:Name="MyWindow">
<Window.Resources>
<ResourceDictionary>
<local:EmptyListVisibilityConverter x:Key="EmptyListVisibilityConverter"></local:EmptyListVisibilityConverter>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button Height="50" Width="100" Content="Button" VerticalAlignment="top" Visibility="{Binding MenuItems, Converter={StaticResource EmptyListVisibilityConverter}}"></Button>
<Grid Width="200" Height="200">
<ItemsControl ItemsSource="{Binding MenuItems}" Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static readonly DependencyProperty MenuItemsProperty =
DependencyProperty.Register(
"MenuItems", typeof(ObservableCollection<MyItem>),
typeof(MainWindow),
new FrameworkPropertyMetadata(default (ObservableCollection<MyItem>),FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
public MainWindow()
{
InitializeComponent();
MenuItems = new ObservableCollection<MyItem>();
MenuItems.Add(new MyItem("Entry 1"));
MenuItems.Add(new MyItem("Entry 2"));
DataContext = this;
}
public ObservableCollection<MyItem> MenuItems
{
get => (ObservableCollection<MyItem>) GetValue(MenuItemsProperty);
set => SetValue(MenuItemsProperty, value);
}
}
public class MyItem
{
public MyItem(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
public class EmptyListVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Trace.WriteLine($"Do conversion");
if (value == null)
{
Trace.WriteLine($"Conversion value was null");
return Visibility.Collapsed;
}
else
{
ICollection list = value as ICollection;
if (list != null)
{
if (list.Count == 0)
{
Trace.WriteLine($"Count is zero");
return Visibility.Hidden;
}
else
{
Trace.WriteLine($"Count is greater than zero");
return Visibility.Visible;
}
}
else
{
Trace.WriteLine($"Was not a collection");
return Visibility.Visible;
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
However, when I set the ElementName context for the ItemsControl and Button, the ItemsControl still displays fine, but the Button is not shown, because the converter for some reason receives an empty collection.
<Button Height="50" Width="100" Content="Button" VerticalAlignment="top" Visibility="{Binding MenuItems, ElementName=MyWindow, Converter={StaticResource EmptyListVisibilityConverter}}"></Button>
...
<ItemsControl ItemsSource="{Binding MenuItems, ElementName=MyWindow}" Focusable="False">
I tried debugging it, but I don't understand what is wrong. I don't know where this empty collection comes from. I also don't understand why the ItemsControl works fine. I feel like either both should work, or neither.

Data binding a button visibility in WPF isn't working

Alot of code incoming, Some of this might be useless because i was trying alot of things to get this working.
I am trying to make the button Export Installer invisible unless the selected installer has a value of TRUE on IsDownloaded
so i'll start with my WPF
DataContext="{Binding Source={StaticResource TheViewModel}}"
Title="MainWindow" Height="458" Width="755">
<Window.Resources>
<ViewModel:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<ViewModel:InstallerColorConverter x:Key="InstallerColorConverter" />
</Window.Resources>
<ListBox Height="300" Grid.Column="0" Grid.Row="0" SelectionMode="Single" ItemsSource="{Binding SelectedProduct.Installers}" SelectedItem="{Binding SelectedInstaller}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse HorizontalAlignment="Right" Fill="{Binding InstallerRelativeToCurrent, Converter={StaticResource InstallerColorConverter}}" Margin="5,5,5,5 " Height="20" Width="20" Stroke="Black"></Ellipse>
<TextBlock Margin="5,0,5,0" Text="{Binding Name}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding VersionNumber}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Stretch">
<Button Name="btnInstall" Margin="5,5,5,5" Height="50" Width="75" MouseDoubleClick="btnDownloadInstaller_MouseDoubleClick" >Download</Button>
<Button Name="btnExportInstaller" Visibility="{Binding Path=SelectedInstaller.IsDownloaded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Button}}, Converter={StaticResource BoolToVisibilityConverter}}" Margin="5,5,5,5" Height="50" Width="90" MouseDoubleClick="btnExportInstaller_MouseDoubleClick" >Export Installer</Button>
</StackPanel>
The main things to point out in that is
<Button Name="btnExportInstaller" Visibility="{Binding Path=SelectedInstaller.IsDownloaded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Button}}, Converter={StaticResource BoolToVisibilityConverter}}" Margin="5,5,5,5" Height="50" Width="90" MouseDoubleClick="btnExportInstaller_MouseDoubleClick" >Export Installer</Button>
and
<ViewModel:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
now in my class BoolToVisibilityConver
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var booool = (bool)value;
if (booool)
return Visibility.Collapsed;
else
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
// Do the conversion from visibility to bool
return Visibility.Collapsed;
}
}
Then finally in the view model i have
private Visibility _IsVisible;
public Visibility IsVisible
{
get { return _IsVisible; }
set { _IsVisible = value; OnPropertyChanged(nameof(_SelectedInstaller.IsDownloaded)); }
}
private Products _SelectedProduct;
public Products SelectedProduct
{
get { return _SelectedProduct; }
set { _SelectedProduct = value; OnPropertyChanged(nameof(SelectedProduct)); }
private Installers _SelectedInstaller;
public Installers SelectedInstaller
{
get { return _SelectedInstaller; }
set { _SelectedInstaller = value; OnPropertyChanged(nameof(SelectedInstaller)); }
It doesn't work
I'm getting these 2 errors on the console and i don't understand them
System.Windows.Data Error: 1 : Cannot create default converter to perform 'one-way' conversions between types 'System.Boolean' and 'System.Windows.Visibility'. Consider using Converter property of Binding. BindingExpression:Path=SelectedProduct.IsInstalled; DataItem='ViewModel' (HashCode=26987408); target element is 'Grid' (Name=''); target property is 'Visibility' (type 'Visibility')
System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='False' BindingExpression:Path=SelectedProduct.IsInstalled; DataItem='ViewModel' (HashCode=26987408); target element is 'Grid' (Name=''); target property is 'Visibility' (type 'Visibility')
If anyone can point out what i'm doing wrong here i would love you forever!
SOLVED..this is the working code
<Window.Resources>
<ViewModel:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<Button Name="btnExportInstaller" Visibility="{Binding SelectedInstaller.IsDownloaded , Converter={StaticResource BoolToVisibilityConverter}}" Margin="5,5,5,5" Height="50" Width="90" MouseDoubleClick="btnExportInstaller_MouseDoubleClick">Export Installer</Button>
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var booool = (bool)value;
if (booool == false)
return Visibility.Collapsed;
else
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value is Visibility && (Visibility)value == Visibility.Visible)
{
return true;
}
return false;
}
}
Inside viewmodel
private Installers _SelectedInstaller;
public Installers SelectedInstaller
{
get { return _SelectedInstaller; }
set { _SelectedInstaller = value; OnPropertyChanged(nameof(SelectedInstaller)); }
private Visibility _IsVisible;
public Visibility IsVisible
{
get { return _IsVisible; }
set { _IsVisible = value; OnPropertyChanged(nameof(SelectedInstaller.IsDownloaded)); }
}

setting delay to update target in Binding in UWP

I bind SelectedValue of my ListView and this binding used converter.
I want that ConvertBack method be executed after a delay, it seems to be easy in WPF but not in UWP.
How can I do this?
If you just want to delay in your ConvertBack, then you can use a task and call Task.Result to return your value. For example like this:
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
var val = value.ToString();
var task = Task.Run(async () =>
{
await Task.Delay(1000);
return val;
});
return task.Result;
}
For the scenario I used this code, I use two way binding to bind the SelectedIndex of a ListView to the Text of a TextBox, here is the demo:
<Page.DataContext>
<local:BlankPage6ViewModel x:Name="ViewModel" />
</Page.DataContext>
<Page.Resources>
<local:IndexToItemConverter x:Key="cvt" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="4*" />
</Grid.RowDefinitions>
<TextBox x:Name="tb" Text="5" Height="50" />
<ListView ItemsSource="{Binding MyItems}"
SelectionMode="Single"
SelectedIndex="{Binding ElementName=tb, Path=Text, Mode=TwoWay, Converter={StaticResource cvt}}" Grid.Row="1">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ID}" />
<TextBlock Text="{Binding Name}" Margin="5,0" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
ViewModel and Model:
public class BlankPage6ViewModel
{
public BlankPage6ViewModel()
{
MyItems = new ObservableCollection<IDModel>();
for (int i = 0; i < 50; i++)
{
MyItems.Add(new IDModel { ID = i, Name = "Name " + i });
}
}
public ObservableCollection<IDModel> MyItems { get; set; }
}
public class IDModel
{
public int ID { get; set; }
public string Name { get; set; }
}
The whole converter is simple like this:
public class IndexToItemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
Debug.WriteLine("CONVERT");
return Int32.Parse(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
var val = value.ToString();
var task = Task.Run(async () =>
{
await Task.Delay(1000);
return val;
});
return task.Result;
}
}
Rendering image of this demo:
There is a very good blog for this scenario, you can have a look: Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding.

Multibinding Error

I posted my question before:
Multi-Forms Binding data
I solved it by building Converter.
XAML:
<Window x:Class="Test_MultiBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="621"
xmlns:c="clr-namespace:Test_MultiBinding">
<Window.Resources>
<c:myConverter x:Key="TestConverter"/>
</Window.Resources>
<Grid>
<TextBox TextWrapping="Wrap" AcceptsReturn="True" Height="269" HorizontalAlignment="Left" Margin="376,22,0,0" Name="textBox1" VerticalAlignment="Top" Width="211" >
<TextBox.Text>
<MultiBinding Converter="{StaticResource TestConverter}">
<Binding ElementName="textBox2" Path="Text"/>
<Binding ElementName="textBox3" Path="Text"/>
<Binding ElementName="textBox4" Path="Text"/>
<Binding ElementName="textBox5" Path="Text"/>
<Binding ElementName="textBox6" Path="Text"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
<TextBox Height="40" HorizontalAlignment="Left" Margin="130,24,0,0" Name="textBox2" VerticalAlignment="Top" Width="222"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="12,22,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBox Height="40" HorizontalAlignment="Left" Margin="130,70,0,0" Name="textBox3" VerticalAlignment="Top" Width="222" />
<TextBox Height="40" HorizontalAlignment="Left" Margin="130,116,0,0" Name="textBox4" VerticalAlignment="Top" Width="222" />
<TextBox Height="40" HorizontalAlignment="Left" Margin="130,162,0,0" Name="textBox5" VerticalAlignment="Top" Width="222" />
<TextBox Height="91" HorizontalAlignment="Left" Margin="130,208,0,0" Name="textBox6" VerticalAlignment="Top" Width="222" />
</Grid>
</Window>
MainWindow:
namespace Test_MultiBinding
{
public partial class MainWindow : Window
{
......
}
[ValueConversion(typeof(string), typeof(string))]
public class myConverter : IValueConverter
{
public Object Convert(object[] value, Type targettype, object parameter, System.Globalization.CultureInfo cultreinfo)
{
return str1 + value[0].ToString() + str2 + value[1].ToString() + str3 + value[2].ToString() + str4 + value[3].ToString() + str5 + value[4].ToString() + str6;
}
public Object ConvertBack(object value, Type targettype, object parameter, System.Globalization.CultureInfo cultreinfo)
{
throw new NotImplementedException();
}
}
}
In which str1,2,3,... are string. When I run it, I got error:
An object of the type "Test_MultiBinding.myConverter" cannot be applied to a property that expects the type "System.Windows.Data.IMultiValueConverter"
Please help!
For a MultiBinding you have to implement the IMultiValueConverter Interface instead of IValueConverter.
public class myConverter : IMultiValueConverter
public class MultiStringConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string result = "";
foreach(object value in values)
result += value.ToString();
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Categories