Want to activate a button only when two inputs of the form are not empty using MAUI - c#

I would like to do the following using C# MAUI.
I want to enable the Save button only if the contents of the email Entry and the phone Entry are both non-null.
The documentation has the following code
<Entry x:Name="email"
Text="" />
<Entry x:Name="phone"
Text="" />
<Button Text="Save">
<Button.Triggers>
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference email},
Path=Text.Length}"
Value="0" />
<BindingCondition Binding="{Binding Source={x:Reference phone},
Path=Text.Length}"
Value="0" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="False" />
<!-- multiple Setter elements are allowed -->
</MultiTrigger>
</Button.Triggers>
</Button>
However, with the above code, the save button will be enabled as long as neither the email nor the phone is null.
How can I change this?

You can add a TextChanged event to the Entry to determine whether the Text is empty. I wrote a simple demo to test it. You can refer to the code:
Xaml:
<Entry x:Name="email" TextChanged="OnTextChanged"/>
<Entry x:Name="phone" TextChanged="OnTextChanged"/>
<Button x:Name="Save" Text="Click Me" Clicked="OnSaveClicked" IsEnabled="False"/>
.cs file:
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(email.Text) && !string.IsNullOrWhiteSpace(phone.Text))
{
Save.IsEnabled = true;
}
else
{
Save.IsEnabled = false;
}
}
Hope it can help you.

There are a couple of ways to achieve this apart from setting the IsEnabled property of the <Button> from the code-behind.
Option 1: Using the MVVM pattern
In your ViewModel, define some properties for E-Mail and Phone, a Command as well as an evaluation function that serves as a predicate for the CanExecute parameter for the Command like follows:
public partial class MyViewModel : ObservableObject
{
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SaveCommand))]
private string email;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SaveCommand))]
private string phone;
[RelayCommand(CanExecute = nameof(CanSave))]
private void Save()
{
// your logic here
}
private bool CanSave() => !string.IsNullOrWhiteSpace(Email) && !string.IsNullOrWhiteSpace(Phone);
}
Then, in the code-behind of your View (the *.xaml.cs file), you need to set the BindingContext to the ViewModel:
public partial class MyPage: ContentPage
{
public MyPage()
{
InitializeComponent();
BindingContext = new MyViewModel();
}
}
Finally, in your View, you can bind the Text property of each Entry to the appropriate property in the ViewModel and bind the Button to the Command:
<Entry Text="{Binding Email, Mode=TwoWay}" />
<Entry Text="{Binding Phone, Mode=TwoWay}" />
<Button Text="Save"
Command="{Binding SaveCommand}">
This way, the Button will only be enabled when both entries contain some text.
You can find more information on the Model-View-ViewModel (MVVM) pattern in the documentation. This example also uses the Source Generators of the MVVM Community Toolkit, which I've also written a blog series about which also covers the topic of enabling and disabling buttons based on property values.
Option 2: Using MultiBinding
Instead of a MultiTrigger, you could use a MultiBinding instead, which allows you evaluate your bindings to either true or false based on both entries.
For this, you first need a converter that implements the IMultiValueConverter interface and checks if all the inputs are of type string and not null, whitespace or empty:
public class AllNotNullOrEmptyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || !targetType.IsAssignableFrom(typeof(bool)))
{
return false;
}
foreach (var value in values)
{
if (value is not string b)
{
return false;
}
if (string.IsNullOrWhiteSpace(b))
{
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return default;
}
}
You can then consume the converter and use a MultiBinding in your XAML:
<ContentPage.Resources>
<converters:AllNotNullOrEmptyConverter x:Key="AllNotNullOrEmptyConverter" />
</ContentPage.Resources>
<VerticalStackLayout>
<Entry x:Name="Email"/>
<Entry x:Name="Phone"/>
<Button>
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource AllNotNullOrEmptyConverter}">
<Binding Path="Text" Source="{x:Reference Email}" />
<Binding Path="Text" Source="{x:Reference Phone}" />
</MultiBinding>
</Button.IsEnabled>
</Button>
</VerticalStackLayout>
The beauty of this approach is that you can add more entries and only need to add one extra line to the MultiBinding for each to include them in the evaluation for the IsEnabled property of the button.

Related

Show/Hide Password MVVM Xamarin Forms Checkbox

I have been trolling this site, youtube and google to find an answer but I only come up with EventTriggers or ImageSource.
I have a LoginPageView and LoginPageViewModel there is an Entry for where the user must put in their password and a CheckBox which allows the option of viewing the password entered.
I have tried numerous ways to bind and code the Checkbox to show the password but I only end up either not succeeding or just permanently showing the password.
I have the code for the .XAML
<Entry Text="{Binding Password}"
TextColor="White"
FontSize="18"
FontAttributes="Bold"
Placeholder="Password"
PlaceholderColor="White"
IsPassword="{Binding IsPass}"
x:Name="PasswordBox"
VerticalTextAlignment="Center"/>
<CheckBox Color="White"
x:Name="ChkShowPass"
Margin="25,0,0,0"
IsChecked="{Binding IsCheckPass}"/>
<Label Text="Show Password"
TextColor="White"
FontAttributes="Bold"
FontSize="17"
RelativeLayout.XConstraint="60"
RelativeLayout.YConstraint="4"/>
Now the options I have tried for LoginPageViewModel are as follows
Option 1
private bool isCheckPass;
public bool IsCheckPass
{
get { return isCheckPass; }
set
{
if (isCheckPass != value)
{
isCheckPass = value;
if (PropertyChanged != null)
{
}
}
}
}
Then tried .XAML
<ImageButton Source="ShowPass.png"
x:Name="BtnShowPass"
Margin="25,0,0,0"
BackgroundColor="Transparent"
Command="{Binding ToggleIsPassword}">
<ImageButton.Triggers>
<DataTrigger TargetType="ImageButton"
Binding="{Binding IsPassword}" Value="True">
<Setter Property="Source" Value="HidePass.png" />
</DataTrigger>
</ImageButton.Triggers>
</ImageButton>
.cs in LoginPageViewModel
private bool _IsPass = true;
public bool IsPass
{
get
{
return _IsPass;
}
set
{
_IsPass = value;
OnPropertyChanged();
OnPropertyChanged("IsPass");
}
}
public ICommand ToggleIsPassword => new Command(() => IsPass = !IsPass);
Please can someone assist here, any and all help welcome.
UPDATE
You need a converter to convert this for that you can use the InverseBoolConverter as shown below:
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();
}
}
Define it in your XAML:
<ContentPage.Resources>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
</ContentPage.Resources>
If I were you I would do something like below:
In your ViewModel add a property:
public bool IsPasswordVisible
{
get => isPassword;
set
{
isPassword = value;
OnPropertyChanged();
}
}
And then In your View, you would Bind them as such:
<Entry Text="{Binding IsPasswordVisible, Converter={StaticResoource InverseBooleanConverter}}"
TextColor="White"
FontSize="18"
FontAttributes="Bold"
Placeholder="Password"
PlaceholderColor="White"
IsPassword="{Binding IsPass}"
x:Name="PasswordBox"
VerticalTextAlignment="Center"/>
<CheckBox Color="White"
x:Name="ChkShowPass"
Margin="25,0,0,0"
IsChecked="{Binding IsPasswordVisible, Mode=TwoWay}"/>
Hope this helps!

How to change page backgroundcolor with switch in Xamarin

I want to change background color on my page when switch is true and false.So far I have
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:App9"
mc:Ignorable="d"
x:Class="App9.MainPage" x:Name="main" BackgroundColor="{Binding Display}">
<ContentPage.BindingContext>
<local:Class1/>
</ContentPage.BindingContext>
<StackLayout>
<Switch x:Name="toggle" Toggled="Switch_Toggled"/>
</StackLayout>
Code behind:
private void Switch_Toggled(object sender, ToggledEventArgs e)
{
if (toggle.IsToggled == true)
{
class1.Display=Color.White;
}
}
And class1
public class Class1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Color display;
public Color Display
{
get
{
return display;
}
set
{
if (display != value)
{
display = value;
NotifyPropertyChanged(nameof(Display));
}
}
}
So when switch is on the background should be white.But it wont changed.I am not sure how to use INotifyPropertyChanged.
I believe the simplest way would be to use triggers for that purpose.
Triggers allow you to express actions declaratively in XAML that change the appearance of controls based on events or property changes. (Source)
You can bind a DataTrigger to the value of a Switch to change the appearance of another control. I built a small example with a Switch that changes the BackgroundColor of a BoxView, just for demonstration purposes:
<StackLayout>
<Switch x:Name="Switch" />
<BoxView BackgroundColor="Crimson">
<BoxView.Triggers>
<DataTrigger TargetType="BoxView"
Binding="{Binding Source={x:Reference Switch}, Path=IsToggled}"
Value="True">
<Setter Property="BackgroundColor" Value="CornflowerBlue" />
</DataTrigger>
</BoxView.Triggers>
</BoxView>
</StackLayout>
I've added a DataTrigger to BoxView.Triggers that reacts to the Switch.IsToggled property. If it's set to True (see the Value property), the Setter will be applied to the BoxView and BackgroundColor will be set to CornflowerBlue.
In your case it'd be something like
<ContentPage.Triggers>
<DataTrigger TargetType="ContentPage"
Binding="{Binding Source={x:Reference Switch}, Path=IsToggled}"
Value="True">
<Setter Property="BackgroundColor" Value="White" />
</DataTrigger>
</ContentPage.Triggers>
There is no need for using BindingContext in this case.
Works:

MultiBinding Color Change on Property Change - How to Clear pragmatically?

Edit: I created a sample project displaying what I have done and what doesn't work. https://github.com/jmooney5115/clear-multibinding
I have a WPF application with controls (textbox, datagrid, etc). When a value changes on the control I need to indicate it by changing the background color. After saving changes the background color needs to go back to the unchanged state without reloading the control. This application is not MVVM, don't judge I inherited it.
I have the code working perfectly for changing the color using MultiBinding and a value converter. The problem is I cannot figure out how to reset the background after calling Save() in my code. I have tried doing DataContext = null and then DataContext = this but the control flickers. There has to be a better way.
Q: how can I reset the background to the unchanged state without reloading the control?
MultiBinding XAML - this works by passing a string[] to BackgroundColorConverter. string[0] is the OneTime binding. string1 is the other binding.
<TextBox.Background>
<MultiBinding Converter="{StaticResource BackgroundColorConverter}">
<Binding Path="DeviceObj.Name" />
<Binding Path="DeviceObj.Name" Mode="OneTime" />
</MultiBinding>
</TextBox.Background>
BackgroundColorConverter.cs
/// <summary>
/// https://stackoverflow.com/questions/1224144/change-background-color-for-wpf-textbox-in-changed-state
///
/// Property changed
/// </summary>
public class BackgroundColorConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var colorRed = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FFB0E0E6");
var colorWhite = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("White");
var unchanged = new SolidColorBrush(colorWhite);
var changed = new SolidColorBrush(colorRed);
if (values.Length == 2)
if (values[0].Equals(values[1]))
return unchanged;
else
return changed;
else
return changed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Updates
Edit: this is the multi binding for a data grid cell. If the multi binding converter returns true, set the background color to LightBlue. If false, the background is the default color.
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<!-- https://stackoverflow.com/questions/5902351/issue-while-mixing-multibinding-converter-and-trigger-in-style -->
<DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource BackgroundColorConverterBool}">
<Binding Path="Name" />
<Binding Path="Name" Mode="OneTime" />
</MultiBinding>
</DataTrigger.Binding>
</DataTrigger>
<Setter Property="Background" Value="LightBlue"></Setter>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
.
.
.
</DataGrid.Columns>
I made this method to reset the binding of objects after saving.
/// <summary>
/// Update the data binding after a save to clear the blue that could be there when
/// a change is detected.
/// </summary>
/// <typeparam name="T">Type to search for</typeparam>
/// <param name="parentDepObj">Parent object we want to reset the binding for their children.</param>
public static void UpdateDataBinding<T>(DependencyObject parentDepObj) where T : DependencyObject
{
if (parentDepObj != null)
{
MultiBindingExpression multiBindingExpression;
foreach (var control in UIHelper.FindVisualChildren<T>(parentDepObj))
{
multiBindingExpression = BindingOperations.GetMultiBindingExpression(control, Control.BackgroundProperty);
if (multiBindingExpression != null)
multiBindingExpression.UpdateTarget();
}
}
}
Final Update
This question answers how to use MultiBinding for my purpose on DataGridCell: Update MultiBinding on DataGridCell
IHMO a MVVM solution (as Rekshino proposed) is for sure better than a not-MVVM one. The view model should take care about tracing modified data.
Anyway since you inherited this application, you have to consider how much time you need for converting the whole code and sometimes it is not possible. So in this case you can force every single multibinding to "refresh" when you save your data.
Let's suppose this is your XAML (with two or more TextBoxes):
<StackPanel>
<TextBox Margin="5" Text="{Binding DeviceObj.Name, Mode=TwoWay}">
<TextBox.Background>
<MultiBinding Converter="{StaticResource BackgroundColorConverter}">
<Binding Path="DeviceObj.Name" />
<Binding Path="DeviceObj.Name" Mode="OneTime" />
</MultiBinding>
</TextBox.Background>
</TextBox>
<TextBox Margin="5" Text="{Binding DeviceObj.Surname, Mode=TwoWay}">
<TextBox.Background>
<MultiBinding Converter="{StaticResource BackgroundColorConverter}">
<Binding Path="DeviceObj.Surname" />
<Binding Path="DeviceObj.Surname" Mode="OneTime" />
</MultiBinding>
</TextBox.Background>
</TextBox>
<Button Content="Save" Click="Button_Click" Margin="5,10,5,10" />
</StackPanel>
When you click the "Save" Button you can force MultiBindings to update their own targets in this way:
private void Button_Click(object sender, RoutedEventArgs e)
{
MultiBindingExpression multiBindingExpression;
foreach (TextBox textBox in FindVisualChildren<TextBox>(this))
{
multiBindingExpression = BindingOperations.GetMultiBindingExpression(textBox, TextBox.BackgroundProperty);
multiBindingExpression.UpdateTarget();
}
}
You can find the FindVisualChildren implementation in this answer. I hope it can help you.
You have to paste a bool Saved property to your DeviceObj and handle it, if Name or something else been changed.
ViewModel:
public class Device : INotifyPropertyChanged
{
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
Saved = false;
NotifyPropertyChanged(nameof(Name));
}
}
}
private string _name;
public bool Saved
{
get
{
return _saved;
}
set
{
if (value != _saved)
{
_saved = value;
NotifyPropertyChanged(nameof(Saved));
}
}
}
private bool _saved = true;
public void Save()
{
//Saving..
Saved = true;
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string info)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
Converter:
public class BoolToSolColBrushConverter : IValueConverter
{
private static SolidColorBrush changedBr = new SolidColorBrush(Colors.Red);
private static SolidColorBrush unchangedBr = new SolidColorBrush(Colors.Green);
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
if ((bool)value)
{
return unchangedBr;
}
}
catch (Exception)
{
}
return changedBr;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
XAML:
<TextBox Text="{Binding Name}" Background="{Binding Saved, Converter={StaticResiurce BoolToSolColBrushConverter}}" />

Add MouseMove event if value is true

I have DataTemplate selector for my ItemsControl and I'd like to achieve something like:
<DataTemplate x:Key="buttonTemplate">
<Button if(someValue = true -> add thisPreviewMouseUp="button_MouseUp") PreviewMouseLeftButtonDown="button_MouseLeftButtonUp" PreviewMouseMove="button_MouseMove" Click="b_Click">
<Button.Content>
<Image Source="sample.png" Height="{Binding height}" Width="{Binding width}" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Button.Content>
<Button.RenderTransform>
<RotateTransform Angle="{Binding angle}" />
</Button.RenderTransform>
</Button>
</DataTemplate>
Users can move, change size of buttons from manager mode, but I don't want to fire this event in normal mode (now there is if(_fromWhere == "MANAGER") in mouse_move event)
Any idea how can I make it work?
Thanks!
Might help.
Mvvm Approach
I think easy way of doing this , Enable the Event only when a condition is true.
what i'm doing here i Execute a a Command Named DropUser when Event Drop Occurs. I used mvvmlight for easy commanding . you can use ICommand Wpf default commanding reference
xmlns:interactivity="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mvvmlight="http://www.galasoft.ch/mvvmlight"
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="Drop">
<mvvmlight:EventToCommand
Command="{Binding DataContext.DropUser,
PassEventArgsToCommand="True"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
ViewModel
#region RelayCommand
private RelayCommand<DragEventArgs> _dropUser;
public RelayCommand<DragEventArgs> DropUser
{
get
{
return _dropUser ?? (_dropUser = new RelayCommand<DragEventArgs>(DropMethod,canExecute));
}
}
private bool canExecute(DragEventArgs arg)
{
// check your condition return true . Command is only work when you return true.
}
#endregion
// Method Will Fire here and do action here
private void DropMethod(DragEventArgs eventArgs)
{
if (eventArgs != null)
{
}
}
what you need is like this.
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="PreviewMouseLeftButtonDown">
<mvvmlight:EventToCommand
Command="{Binding CommandName,
PassEventArgsToCommand="True"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
Viewmodel
private RelayCommand<RoutedEventArgs> _commandName;
public RelayCommand<RoutedEventArgs>CommandName
{
get
{
return commandName ?? (commandName = new RelayCommand<RoutedEventArgs>(invokeAction, canExecuteMethod));
}
}
private bool canExecuteMethod()
{
check your condition return true
}
private void invokeAction(RoutedEventArgs event)
{
action you want to do
}
I don't know, if it's possible in XAML to set event to be fired or not.
However you can set someValue in Tag property of button and then in fired event check for Tag as proposed in this link.
<Button Tag="{Binding someValue}" ...>
In event handler:
bool someValue = (bool)(((Button)sender).Tag);
Edit 1:
An alternative way would be to get the DataContext of button if someValue is part of DataContext of button:
var dataContext = ((Button)e.OriginalSource).DataContext;
if (dataContext.someValue)
{
...
}
Edit 2:
After a little investigation I found out that it is possible to add MouseMove event depending on someValue. Unfortunately it is unnecessarily complex I think.
<Button Style="{Binding someValue, Converter={StaticResource BoolToButtonStyleConverter}, ConverterParameter={StaticResource MousePreviewEventSetStyle}, Mode=OneWay}" ...>
In Window.Resources define this:
<l:BoolToButtonStyleConverter x:Key="BoolToButtonStyleConverter" />
<Style x:Key="MousePreviewEventSetStyle" TargetType="{x:Type Button}" >
<EventSetter Event="PreviewMouseUp" Handler="PreviewMouseUpClicked" />
</Style>
And then in Application.Resources define this:
<Style x:Key="clearStyle" TargetType="{x:Type Button}" />
After all you have to define:
public class BoolToButtonStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
bool someValue = (bool)value;
if (someValue)
return parameter;
else
return Application.Current.Resources["clearStyle"];
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Finally set the handler for event:
void PreviewMouseUpClicked(object sender, RoutedEventArgs e)
{
...
}
Memo: Probably there is better way to set "default style" to button that define "clear style".

Binding a Button's visibility to a bool value in ViewModel

How do I bind the visibility of a button to a bool value in my ViewModel?
<Button Height="50" Width="50" Style="{StaticResource MyButtonStyle}"
Command="{Binding SmallDisp}" CommandParameter="{Binding}" Cursor="Hand"
Visibility="{Binding Path=AdvancedFormat}" />
Assuming AdvancedFormat is a bool, you need to declare and use a BooleanToVisibilityConverter:
<!-- In your resources section of the XAML -->
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<!-- In your Button declaration -->
<Button
Height="50" Width="50"
Style="{StaticResource MyButtonStyle}"
Command="{Binding SmallDisp}" CommandParameter="{Binding}"
Cursor="Hand" Visibility="{Binding Path=AdvancedFormat, Converter={StaticResource BoolToVis}}"/>
Note the added Converter={StaticResource BoolToVis}.
This is a very common pattern when working with MVVM. In theory you could do the conversion yourself on the ViewModel property (i.e. just make the property itself of type Visibility) though I would prefer not to do that, since now you are messing with the separation of concerns. An item's visbility should really be up to the View.
There's a third way that doesn't require a converter or a change to your view model: use a style:
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
I tend to prefer this technique because I use it in a lot of cases where what I'm binding to is not boolean - e.g. displaying an element only if its DataContext is not null, or implementing multi-state displays where different layouts appear based on the setting of an enum in the view model.
2 way conversion in c# from boolean to visibility
using System;
using System.Windows;
using System.Windows.Data;
namespace FaceTheWall.converters
{
class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Boolean && (bool)value)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Visibility && (Visibility)value == Visibility.Visible)
{
return true;
}
return false;
}
}
}
Since Windows 10 15063 upwards
Since Windows 10 build 15063, there is a new feature called "Implicit Visibility conversion" that binds Visibility to bool value natively - There is no need anymore to use a converter.
(see https://social.technet.microsoft.com/wiki/contents/articles/34846.uwp-compiled-binding-windows-10-anniversary-update.aspx#Implicit_Visibility_conversion).
My code (which supposes that MVVM is used, and Template 10 as well):
<!-- In XAML -->
<StackPanel x:Name="Msg_StackPanel" Visibility="{x:Bind ViewModel.ShowInlineHelp}" Orientation="Horizontal" Margin="0,24,0,0">
<TextBlock Text="Frosty the snowman was a jolly happy soul" Margin="0,0,8,0"/>
<SymbolIcon Symbol="OutlineStar "/>
<TextBlock Text="With a corncob pipe and a button nose" Margin="8,0,0,0"/>
</StackPanel>
<!-- in companion View-Model -->
public bool ShowInlineHelp // using T10 SettingsService
{
get { return (_settings.ShowInlineHelp); }
set { _settings.ShowInlineHelp = !value; base.RaisePropertyChanged(); }
}
Generally there are two ways to do it, a converter class or a property in the Viewmodel that essentially converts the value for you.
I tend to use the property approach if it is a one off conversion. If you want to reuse it, use the converter. Below, find an example of the converter:
<ValueConversion(GetType(Boolean), GetType(Visibility))> _
Public Class BoolToVisibilityConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
If value IsNot Nothing Then
If value = True Then
Return Visibility.Visible
Else
Return Visibility.Collapsed
End If
Else
Return Visibility.Collapsed
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New NotImplementedException
End Function
End Class
A ViewModel property method would just check the boolean property value, and return a visibility based on that. Be sure to implement INotifyPropertyChanged and call it on both the Boolean and Visibility properties to updated properly.
This can be achieved in a very simple way
1. Write this in the view.
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="50" Height="30">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsHide}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
The following is the Boolean property which holds the true/ false value. The following is the code snippet. In my example this property is in UserNote class.
public bool _isHide = false;
public bool IsHide
{
get { return _isHide; }
set
{
_isHide = value;
OnPropertyChanged("IsHide");
}
}
This is the way the IsHide property gets the value.
userNote.IsHide = userNote.IsNoteDeleted;
In View:
<Button
Height="50" Width="50"
Style="{StaticResource MyButtonStyle}"
Command="{Binding SmallDisp}" CommandParameter="{Binding}"
Cursor="Hand" Visibility="{Binding Path=AdvancedFormat}"/>
In view Model:
public _advancedFormat = Visibility.visible (whatever you start with)
public Visibility AdvancedFormat
{
get{return _advancedFormat;}
set{
_advancedFormat = value;
//raise property changed here
}
You will need to have a property changed event
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged.Raise(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
This is how they use Model-view-viewmodel
But since you want it binded to a boolean, You will need some converter.
Another way is to set a boolean outside and when that button is clicked then set the property_advancedFormat to your desired visibility.

Categories