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
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
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.
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)); }
}
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.
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();
}
}