Toggle a grid by clicking a button - c#

I'm trying to collapse a Grid by clicking on a Button. This is how my Button is represented in xaml:
<Button Grid.Column="1" Grid.RowSpan="3" Width="25" Content="<<" Click="OnClicked" x:Name="btnCollapse"></Button>
I want to collapse a Grid on clicking this Button (something like a docked window) and bring back the Grid on clicking on the Button again. This is how I do that in the code behind:
private bool clicked;
private void OnClicked(object sender, RoutedEventArgs e)
{
clicked = !clicked;
//leftPane is my grid
leftPane.Visibility = clicked ? Visibility.Collapsed:Visibility.Visible;
btnCollapse.Content = clicked ? ">>" : "<<";
}
This works fine, my question is how can I represent this logic purely in xaml rather than in the code behind?
My layout:
<Grid>
<Grid/>
<GridSplitter/>
</Grid>
<Button/>

You should bind the Visibility dependency property of your Grid to a boolean property in the DataContext (which should implement INotifyPropertyChanged) and use a BooleanToVisibilityConverter:
private bool _isGridVisible;
public bool IsGridVisible
{
get { return _isGridVisible; }
set
{
if (_isGridVisible != value)
return;
_isGridVisible = value;
OnPropertyChanged("IsGridVisible"); // This can sometimes be named RaisePropertyChanged
}
}
private void OnClick(object sender, RoutedEventArgs e)
{
IsGridVisible = !IsGridVisible;
}
In XAML:
<Grid Visibility="{Binding IsGridVisible, Converter={StaticResource BooleanToVisibilityConverter}">
<!-- stuff -->
</Grid>
How to set button content:
IValueConverter:
public class MyBooleanToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? ">>" : "<<";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<Button Click="OnClick" Content="{Binding IsGridVisible, Converter={StaticResource MyBooleanToStringConverter}}"/>
Style:
<Button Click="OnClick">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content" Value="<<"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsGridVisible}" Value="False">
<Setter Property="Content" Value=">>"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Related

WPF Trigger based on DataGridCell Value Type

What i am trying is
<local:class_converter_data_type x:Key="DataTypeConverter"/>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource DataTypeConverter}}" Value="{x:Type sys:DateTime}">
<Setter Property="FontStyle" Value="Italic" />
</DataTrigger>
</Style.Triggers>
</Style>
public class class_converter_data_type : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value?.GetType() ?? Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
What errors i am making here? Binding format has any errors or in conversion?
You should not do it like this. The DataGridCell does not directly contain any type information of the underlying data type. The cleanest way is to listen to the DataGrid.AutoGeneratingColumn event and configure the appearance of the column's content:
MainWindow.xaml
<DataGrid AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" />
MainWIndow.xaml.cs
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var dataGrid = sender as DataGrid;
if (e.PropertyType.Equals(typeof(DateTime))
&& e.Column is DataGridTextColumn column)
{
column.FontStyle = FontStyles.Italic;
}
}
In case you are not auto-generating the columns, you can set the font style by setting the DataGridTextColumn.FontStyle XAML attribute.
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn FontStyle="Italic" />
</DataGrid.Columns>
</DataGrid>

Binding the style of the control to a Viewmodel property

I am trying to create a banner with different states and colors to show messages to the user, showing a Border with a specific style and a Label with an style bases in the Border style and using DataTrigger. I have created each custom styles for each state in my App.xaml and I am trying to change the state based on a property of my ViewModel.
The problem is that the style doesn't change every time I change the property, but nevertheless if I modify some of the XAML during debugging, the style is refreshed correctly.
Maybe I am missing a NotifyPropertyChanged somewhere?
Visual example:
This is My code:
App.xaml with my defined styles and my custom converter
<Application.Resources>
<local:StyleConverter x:Key="StyleConverter" />
<Style x:Key="Banner" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="10" />
<Setter Property="CornerRadius" Value="5" />
</Style>
<Style x:Key="Banner_Info" TargetType="{x:Type Border}" BasedOn="{StaticResource Banner}">
<Setter Property="Background" Value="#e3f7fc" />
<Setter Property="BorderBrush" Value="#8ed9f6" />
</Style>
<Style x:Key="Banner_Error" TargetType="{x:Type Border}" BasedOn="{StaticResource Banner}">
<Setter Property="Background" Value="#ffecec" />
<Setter Property="BorderBrush" Value="#f5aca6" />
</Style>
<Style x:Key="Banner_Success" TargetType="{x:Type Border}" BasedOn="{StaticResource Banner}">
<Setter Property="Background" Value="#e9ffd9" />
<Setter Property="BorderBrush" Value="#a6ca8a" />
</Style>
<Style x:Key="Banner_Text" TargetType="{x:Type Label}">
<Setter Property="FontWeight" Value="DemiBold"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding BorderBrush.Color, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}}}" Value="#8ed9f6">
<Setter Property="Foreground" Value="#31708F" />
</DataTrigger>
<DataTrigger Binding="{Binding BorderBrush.Color, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}}}" Value="#f5aca6">
<Setter Property="Foreground" Value="#B10009" />
</DataTrigger>
<DataTrigger Binding="{Binding BorderBrush.Color, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}}}" Value="#a6ca8a">
<Setter Property="Foreground" Value="#529214" />
</DataTrigger>
</Style.Triggers>
</Style>
</Application.Resources>
Styleconverter.cs
public class StyleConverter : IValueConverter {
public object Convert (object value, Type targetType, object parameter, CultureInfo culture) {
if (targetType != typeof (Style)) {
throw new InvalidOperationException ("The target must be a Style");
}
var styleProperty = parameter as string;
if (value == null || styleProperty == null) {
return null;
}
string styleValue = value.GetType ()
.GetProperty (styleProperty)
.GetValue (value, null)
.ToString ();
if (styleValue == null) {
return null;
}
Style newStyle = (Style) Application.Current.TryFindResource (styleValue);
return newStyle;
}
public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException ();
}
}
Banner.cs
public class Banner : INotifyPropertyChanged {
private string _Message;
public string Message {
get => _Message;
private set {
_Message = value;
RaisePropertyChanged (null);
}
}
private string _Style = "Banner_Success";
public string Style {
get => _Style;
private set {
_Style = value;
RaisePropertyChanged (null);
}
}
public Banner SetSuccess (string message) {
Style = "Banner_Success";
Message = message;
return this;
}
public Banner SetInfo (string message) {
Style = "Banner_Info";
Message = message;
return this;
}
public Banner SetError (string message) {
Style = "Banner_Error";
Message = message;
return this;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged (string PropertyName) {
PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (PropertyName));
}
#endregion
}
Mainwindow.xml
<Grid>
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="10*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Grid.Column="1" Grid.Row="0" VerticalAlignment="Center" Style="{Binding ., Mode=TwoWay, Converter={StaticResource StyleConverter}, ConverterParameter=BannerStyle}">
<Label Grid.Column="1" Style="{StaticResource Banner_Text}" Content="{Binding BannerMessage}" />
</Border>
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Padding="10" Margin="10" Content="BLUE" Command="{Binding CmdChangeColor, UpdateSourceTrigger=PropertyChanged}" CommandParameter="blue" />
<Button Padding="10" Margin="10" Content="RED" Command="{Binding CmdChangeColor, UpdateSourceTrigger=PropertyChanged}" CommandParameter="red" />
<Button Padding="10" Margin="10" Content="GREEN" Command="{Binding CmdChangeColor, UpdateSourceTrigger=PropertyChanged}" CommandParameter="green" />
</StackPanel>
</StackPanel>
</Grid>
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged {
public Banner Banner { get; } = new Banner ();
public string BannerStyle => Banner.Style;
public string BannerMessage => Banner.Message;
public RelayCommand CmdChangeColor { get; }
public MainViewModel () {
Banner.SetSuccess ("This is the initial message");
CmdChangeColor = new RelayCommand (param => ChangeColor (param.ToString ()));
}
public void ChangeColor (string color) {
switch (color) {
case "blue":
Banner.SetInfo ("INFO!! This should be a banner with blue background");
break;
case "red":
Banner.SetError ("ERROR!! This should be a banner with red background");
break;
case "green":
Banner.SetSuccess ("SUCCESS!! This should be a banner with green background");
break;
}
RaisePropertyChanged (null);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged (string PropertyName) {
PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (PropertyName));
}
#endregion
}
bind to property which has notifications, instead of entire view model ({Binding .}). This way also simplifies converter (no more reflection)
<Border Style="{Binding Path=Banner.Style, Converter={StaticResource StyleConverter}">
public class StyleConverter : IValueConverter {
public object Convert (object value, Type targetType, object parameter, CultureInfo culture) {
if (targetType != typeof (Style)) {
throw new InvalidOperationException ("The target must be a Style");
}
string styleValue = value?.ToString();
if (styleValue == null) {
return null;
}
Style newStyle = (Style) Application.Current.TryFindResource (styleValue);
return newStyle;
}
public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException ();
}
}

BooleanToVisibilityConverter that converts to Visible when true or null and Collapsed when false

I do some binding between a ThreeState ToggleButton and a normal button
that's what I'm trying to achieve:
button#1 Visible if button#2 IsChecked=True or Null
button#1 Collapsed if button#2 IsChecked=False
The built-in BooleanToVisibilityConverter shows the button only when true.
I would be very grateful if you could help with the Converter.
Thanks
This is ridiculously simple. The following IValueConverter returns Visibility.Visible when value is either true or null:
public sealed class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool bValue && bValue
|| value == null
? Visibility.Visible
: Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is Visibility visibility
&& visibility == Visibility.Visible;
}
}
And without BooleanToVisibilityConverter, only XAML :
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Visibility"
Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=chk1, Path=IsChecked}"
Value="False">
<Setter Property="Visibility"
Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Button Content="Button"
HorizontalAlignment="Left"
Margin="34,68,0,0"
VerticalAlignment="Top"
Width="75" />
<CheckBox x:Name="chk1"
Content=" Toggle"
HorizontalAlignment="Left"
Margin="34,29,0,0"
VerticalAlignment="Top"
IsThreeState="True"
IsChecked="True" />
</Grid>

WPF Databinding DataTrigger to change color of shape based on boolean value

I am trying to change the color of a shape by means of databinding and data trigger.
But i am still new to WPF and all.
Let me illustrate with an example. this is a group box
<GroupBox x:Class="Server.Host.SingleAxisControls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:host="clr-namespace:Server.Host"
mc:Ignorable="d"
d:DesignWidth="200">
<Grid>
<StackPanel Orientation="Vertical" Width="180" >
<host:MyRectangleControl x:Name="MyRectangle" />
<Button Click="OnButton_Click" Width="80" Margin="20,5,20,5">On</Button>
<Button Click="OffButton_Click" Width="80">Off</Button>
</StackPanel>
</Grid>
</GroupBox>
MyRectangleControl is a usercontrol of something like
<UserControl x:Class="Server.Host.MyRectangleControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="30">
<Grid>
<Rectangle HorizontalAlignment="Center"
Height="25"
Margin="0,0,0,0"
Stroke="Black"
VerticalAlignment="Center"
Width="25"
Fill="red">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Test,UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="Fill"
Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
In the code behind the groupbox, I have something like
namespace Server.Host
{
public partial class SingleAxisControls : INotifyPropertyChanged
{
public SingleAxisControls()
{
InitializeComponent();
MyRectangle.DataContext = this;
}
private bool _test;
public bool Test
{
get { return _test; }
set
{
_test = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Test"));
}
}
}
private void OnButton_Click(object sender, RoutedEventArgs e)
{
Test = true;
}
private void OffButton_Click(object sender, RoutedEventArgs e)
{
Test = false;
}
}
I am not sure what is wrong but it doesn't seem change the color of the rectangle when i change the value of test from false to true.
This is a problem of value precedence.
When you set a DependencyProperty directly in the declaration of an Element this value has higher precedence than a value set in a style.
All you have to do is set the Fill property to Red in the Style:
<Rectangle HorizontalAlignment="Center"
Height="25"
Margin="0,0,0,0"
Stroke="Black"
VerticalAlignment="Center"
Width="25"
>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Test,UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="Fill"
Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
Another valid option is writing a converter to the binding. It would look like this:
class BooleanToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, System.Globalization.CultureInfo culture)
{
if ((bool)value)
{
return new SolidColorBrush(Colors.Black);
}
return new SolidColorBrush(Colors.LightGray);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
(You can set the colors to anything you want)
and the Xaml would look like this:
<Rectangle HorizontalAlignment="Center"
Height="25"
Margin="0,0,0,0"
Stroke="Black"
VerticalAlignment="Center"
Width="25"
Fill="{Binding Test, Converter={StaticResource b2b}}">
with this at the top of your xaml:
<UserControl.Resources>
<BooleanToBrushConverter x:Key="b2b" />
</UserControl.Resources>
Note: if your converter is in a different location you will need to include it in the namespace and preface the declaration with whatever you named the namespace i.e.
xlmns:converters="clr-namespace:Project.Converters"
in the <UserControl> tag
and then <converters: BooleanToBrushConverter x:Key="b2b" /> in the resources

CheckBox Show/Hide TextBox WPF

As the title says, Iam trying to show/hide a TextBox in WPF without writing code in MainWindow.xaml.cs file.
Model:
public class Person
{
public string Comment { get; set; }
}
View:
<Window x:Class="PiedPiper.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="WPF" Height="400" Width="400">
<CheckBox Content="Show comment" Name="CommentCheckBox"/>
<TextBox Text="{Binding Comment, UpdateSourceTrigger=PropertyChanged}" Visibility="Hidden" Name="CommentTextBox"></TextBox>
</Grid>
ViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel(Person person)
{
Comment = person.Comment;
}
private string _comment;
public string Comment
{
get { return _comment; }
set { _comment = value; OnPropertyChanged("Comment"); }
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
So the TextBox should be hidden at start, but visible when checkbox is checked. Please help!
Thanks.
You can bind TextBox.Visiblity to CheckBox.IsChecked. If you want to toggle between Hidden and Visible then you need to either write custom IValueConverter or create simple Style.Trigger
<StackPanel>
<CheckBox Content="Show comment" Name="CommentCheckBox"/>
<TextBox Text="{Binding Comment, UpdateSourceTrigger=PropertyChanged}" Name="CommentTextBox">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=CommentCheckBox, Path=IsChecked}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
if you want to toggle between Collapsed and Visible there is an easier way and you can use build in BooleanToVisibilityConverter
<StackPanel>
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</StackPanel.Resources>
<CheckBox Content="Show comment" Name="CommentCheckBox"/>
<TextBox
Text="{Binding Comment, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=CommentCheckBox, Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"
Name="CommentTextBox"/>
</StackPanel>
The simplest way is to write a custom "BooleanToHiddenVisibilityConverter" and use it (like dkozl said).
It's a really simple converter and it comes in handy in many situations. I think that every descent WPF application should have one.
public sealed class BooleanToHiddenVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool bValue = false;
if (value is bool)
{
bValue = (bool)value;
}
return (bValue) ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility)
{
return (Visibility)value == Visibility.Visible;
}
return false;
}
}
And use it like dkozl said:
<StackPanel>
<StackPanel.Resources>
<BooleanToHiddenVisibilityConverter x:Key="BooleanToHiddenVisibilityConverter"/>
</StackPanel.Resources>
<CheckBox Content="Show comment" Name="CommentCheckBox"/>
<TextBox
Text="{Binding Comment, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=CommentCheckBox, Path=IsChecked,
Converter={StaticResource BooleanToHiddenVisibilityConverter}}"
Name="CommentTextBox"/>
</StackPanel>

Categories