WPF Visibility Binding to Converted Instance Property - c#

So I have a button. I want to set the visibility of the button according to the value of an integer property of a class. This requires a data binding and a converter.
The XAML code for the button is as follows:
<Window.Resources>
<local:Button1VisibilityConverter x:Key="Button1VisibilityConverter"/>
<local:ModeValues x:Key="ModeHolder"/>
</Window.Resources>
<Grid>
<StackPanel HorizontalAlignment="Left" Height="150" Margin="92,90,0,0" VerticalAlignment="Top" Width="301">
<Button Content="1" Height="58" Background="#FFA20000" Foreground="White" Visibility="{Binding Source={StaticResource ModeHolder}, Path=State, Converter=Button1VisibilityConverter}"/>
<Button Content="2" Height="58" Background="#FF16A200" Foreground="White"/>
<Button Content="3" Height="58" Background="#FF4200A2" Foreground="White"/>
</StackPanel>
</Grid>
My converter is as follows:
class Button1VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targettype, object parameter, System.Globalization.CultureInfo culture)
{
int mode = (int)value;
if (mode == ModeValues.Red)
return System.Windows.Visibility.Visible;
else
return System.Windows.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
The class that has the property I want to control the visibility is as follows:
public class ModeValues : IObservable<int>
{
private int _state = -1;
public static int Red
{
get
{
return 0;
}
}
public static int Green
{
get
{
return 1;
}
}
public static int Purple
{
get
{
return 2;
}
}
public int State
{
get
{
return this._state;
}
set
{
this.State = value;
}
}
}
I have no idea why it isn't working. I thought I had to bind the visibility to the property of the instance of the ModeHolder, make the ModeHolder observable, and convert the int to a visibility. What am I missing?

Converter=Button1VisibilityConverter
should be:
Converter={StaticResource Button1VisibilityConverter}

Related

How to bind UI color attribute with class property

I have a user control which is an ellipse that acts like a "led". I want to bind its "Fill" to a boolean property (State).
I used for that a boolean to Color converter.
here is the user control I did:
<UserControl x:Class="Sol.Components.Led"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...
xmlns:conv="clr-namespace:Sol.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Ellipse Fill="{Binding Converter={StaticResource BoolToColor}}" StrokeThickness="3" Stroke="Gray"/>
</Grid>
</UserControl>
also the converter is not recognised in the user control! I did it like this
public class BoolToColor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
if ((bool)value == true)
return Colors.Green; // to replace with onColor
else
return Colors.Red; // to replace with offColor
}
return Colors.LightGray;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Color)
{
if ((Color)value == Colors.Green) // to compare with onColor
{
return true;
}
}
return false;
}
}
I used a window to include 4 user contols:
<Window x:Class="Sol.Menu.Leds"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:led="clr-namespace:Sol.Components"
xmlns:conv="clr-namespace:Sol.Converters"
Title="Leds" Height="300" Width="300">
<Window.Resources>
<conv:BoolToColor x:Key="BoolToColor" />
</Window.Resources>
<Grid>
...
<led:Led Grid.Column="0" Grid.Row="0" x:Name="led1" State="False"/>
<led:Led Grid.Column="0" Grid.Row="1" x:Name="led2" State="False"/>
<led:Led Grid.Column="1" Grid.Row="0" x:Name="led3" State="False"/>
<led:Led Grid.Column="1" Grid.Row="1" x:Name="led4" State="False"/>
</Grid>
</Window>
and the used control class :
public partial class Led : UserControl
{
private bool state;
public bool State
{
get { return state; }
set { state = value; }
}
private Color onColor;
public Color OnColor
{
get { return onColor; }
set { onColor = value; }
}
private Color offColor;
public Color OffColor
{
get { return offColor; }
set { offColor = value; }
}
public Led()
{
InitializeComponent();
}
}
this is works without binding and the window shows 4 ellipses, but I am unable to change the color dynamically (from the code bedhind).
any help to fix the binding?
Try to bind to the State property of the UserControl:
<Ellipse Fill="{Binding Path=State,
RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource BoolToColor}}" StrokeThickness="3" Stroke="Gray"/>
You should also return a Brush instead of a Color from your converter:
public class BoolToColor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
if ((bool)value == true)
return Brushes.Green; // to replace with onColor
else
return Brushes.Red; // to replace with offColor
}
return Brushes.LightGray;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Brush)
{
if ((Brush)value == Brushes.Green) // to compare with onColor
{
return true;
}
}
return false;
}
}
You need to implement PropertyChanged so the UI knows a property has been changed.
Read here how it should be done: https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-property-change-notification
Fill, like all other UI "color" properties, is actually a Brush value, not a Color
Change your converter to return Brushes.Red / Brushes.Green.

How to update view element display based on property`s value change by VM method?

Need some help. Hello everyone. I really have no idea how to update view elements, when property value is updated with my Egzecute method inside of MsgViewModel, called with public ICommand Start. For example, I want to make one button STOP visible, another one START collapsed, when a property Status changes its value, from Stopped to Sending. Please also be noted, that visibility is updated corrctly when property Status is changed with ViewModels constructor by (default on start for me)Status = Models.SendingStatus.Stopped; or Status = Models.SendingStatus.Sending;.
View:
<!--START, to be collapsed-->
<Button Grid.Row="0"
Grid.Column="4"
Background="#80B584"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Mode=OneWay,
Converter={StaticResource boolStart}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="START" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
<!--STOP, to be viewed-->
<Button Grid.Row="0"
Background="#FF8A8A"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Mode=OneWay,
Converter={StaticResource boolStop}}" Margin="0,145,443.667,-0.333">
<TextBlock Text="STOP" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
ViewModel:
private Models.MsgModel message= new Models.MsgModel (); //model instance
public MsgViewModel() //constructor, by default makes staus "Stopped"
{
Status = Models.SendingStatus.Stopped;
}
public Models.SendingStatus Status
{
get
{
return message.Status;
}
set
{
message.Status = value;
}
}
private ICommand start;
public ICommand Start //command called by START button, supposed to collapse it, and show STOP button
{
get
{
if (start == null)
start = new RelayCommand(
o =>
{
Egzecute();
});
return start;
}
}
public void Egzecute() //method called by the command
{
Status = Models.SendingStatus.Sending;
var openDialog = new Powiadomienie();
openDialog.ShowPowiadomienie(Status.ToString(), "Powiadomienie"); //shows updated SendingStatus, but the View is not updating to it
}
Model:
public enum SendingStatus: byte { Sending, Waiting, Stopped} //enum for Status property
public class MsgModel : INotifyPropertyChanged
private SendingStatus status;
public SendingStatus Status //Status model property
{
get
{
return status;
}
set
{
status = value;
OnPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(params string[] propertyNames)
{
if (PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Converters:
public class BooleanStart : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ViewModels.MsgViewModel mvm = new ViewModels.MsgViewModel();
bool bvalue = (bool)value;
if (mvm.Status == Models.SendingStatus.Sending|| mvm.Status == Models.SendingStatus.Waiting)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanStop : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ViewModels.MsgViewModel mvm = new ViewModels.MsgViewModel();
bool bvalue = (bool)value;
if (mvm.Status == Models.SendingStatus.Sending|| mvm.Status == Models.SendingStatus.Waiting)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
My question is, how to have updated View after calling the method by command?
Ok, after several hours I figured out my mistake. Construction of Converter was wrong. Binding supposed to be different, and ViewModel updated with property change notification.
Converters:
public class BooleanStart : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Models.SendingStatus sendingStatus = (Models.SendingStatus)value;
if (sendingStatus == Models.SendingStatus.Sending || sendingStatus == Models.SendingStatus.Waiting)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanStop : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Models.SendingStatus sendingStatus = (Models.SendingStatus)value;
if (sendingStatus == Models.SendingStatus.Sending || sendingStatus == Models.SendingStatus.Waiting)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
For binding:
<!--START-->
<Button Grid.Row="0"
Grid.Column="4"
Background="#80B584"
Visibility="{Binding Path=Status, Converter={StaticResource boolStart}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="START" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
<!--STOP-->
<Button Grid.Row="0"
Background="#FF8A8A"
Visibility="{Binding Path=Status, Converter={StaticResource boolStop}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="STOP" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
ViewModel` method:
public void Egzecue()
{
Status = Models.SendingStatus.Sending;
OnPropertyChanged("Status");
var openDialog = new Powiadomienie();
openDialog.ShowPowiadomienie(Status.ToString(), "Powiadomienie");
}

Cannot bind DependcyProperty of Model from collection in WPF

In WPF project I use MVVM pattern.
I try to bind an item in a collection to UserControl but everything gets default value of DependcyProperty.
The Window xaml:
<ListView VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
ItemsSource="{Binding Sessions}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="DebugTextBlock" Background="Bisque"
Text="{Binding Connection}"/>
<usercontrol:SessionsControl Model="{Binding Converter=
{StaticResource DebugConverter}}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Where Sessions is
private ObservableCollection<SessionModel> _sessions;
public ObservableCollection<SessionModel> Sessions
{
get { return _sessions; }
set
{
if (Equals(value, _sessions)) return;
_sessions = value;
OnPropertyChanged("Sessions");
}
}
The SessionModel:
public class SessionModel:ViewModelBase
{
private string _connection;
public string Connection
{
get { return _connection; }
set
{
if (value == _connection) return;
_connection = value;
OnPropertyChanged("Connection");
}
}
}
In the SessionsControl I create DependencyProperty:
//Dependency Property
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(SessionModel),
typeof(SessionsControl), new PropertyMetadata(new SessionModel("default_from_control")));
// .NET Property wrapper
public SessionModel Model
{
get { return (SessionModel)GetValue(ModelProperty); }
set { if (value != null) SetValue(ModelProperty, value); }
}
and use this xaml to display connection in form:
<TextBlock Name="DebugControlTextBlock" Background="Gray" Text="{Binding Connection}"/>
So, when I run application
var windowModel = new WindowsModel();
var window = new SessionWindow(windowModel);
window.ShowDialog();
I always get default_from_control value in DebugControlTextBlock, but in DebugTextBlock get the_real_connection
Even if I set breakpoint in DebugConverter I see that value is default.
The DebugConverter is simply wrapper to check correct binding:
public class DebugConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("DebugConverter: " + (value!=null?value.ToString():"null"));
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
See solution on github.
So, what happend when I Binding model to DependcyProperty?
I would suggest that you try to make you Sessions property a DependencyProperty too. Otherwise you might have to manually Raise PropertyChanged for Sessions property.

set Image source from a resource dictionary

I have a wpf application ,in which I have a tabcontrol ,I set its ItemTemplate from an external resource dictionary,in that template I have an Image Control which I bind to a string Property 'ImagePath' in my ViewModel and using a converter I convert 'ImagePath' into new bitmapImage .I created my converter class instance in the external resource disctinary. I merged the external dictionary in my App.xaml
Here is my External Resource Dictionary:
<ResourceDictionary 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:helpers="clr-namespace:RH_Maize.Helper">
<helpers:ImageSourceConverter x:Key="ImageSourceConverter" />
<DataTemplate x:Key="ClosableTabItemTemplate">
<StackPanel Width="155"
Orientation="Horizontal"
Height="35">
<Image Height="25"
Width="30"
Source="{Binding Path=ImagePath,Converter={ StaticResource ImageSourceConverter}}"/>
<TextBlock TextWrapping="Wrap" Text="{Binding DisplayName}"
Width="100" Height="27" VerticalAlignment="Bottom" FontSize="15" Margin="3,0,0,0"/>
<Button Command="{Binding CloseCommand}"
Content="x" />
</StackPanel>
</DataTemplate>
Here is my Converter class :
public class ImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType == typeof(ImageSource))
{
if (value is string)
{
string str = (string)value;
return new BitmapImage(new Uri(str, UriKind.RelativeOrAbsolute));
}
else if (value is Uri)
{
Uri uri = (Uri)value;
return new BitmapImage(uri);
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
My ViewModel Property is :
private string _ImagePath;
public string ImagePath
{
get
{
return _ImagePath;
}
set
{
_ImagePath = value;
RaisePropertyChanged(() => ImagePath);
}
}
My problem is that the converter classes convert method is not invoking at all .Whats wrong with my code ?

WPF radio button binding - getting an error in this code here?

Trying to get radiobuttons binding working but getting a run time error with the code below. Want the radio buttons to act such that only one can be selected at a time, and that they bind correctly in a 2 way fashion. Error text is.
"The invocation of the constructor on
type 'testapp1.MainWindow' that
matches the specified binding
constraints threw an exception"
<Window x:Class="testapp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:testapp1" Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<l:EnumBooleanConverter x:Key="enumBooleanConverter" />
</Grid.Resources>
<StackPanel >
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=FirstSelection}">first selection</RadioButton>
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=TheOtherSelection}">the other selection</RadioButton>
<RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=YetAnotherOne}">yet another one</RadioButton>
<Label Content="{Binding Path=VeryLovelyEnum}" Height="28" Name="label1" />
</StackPanel>
</Grid>
</Window>
And code:
namespace testapp1
{
public partial class MainWindow : Window
{
public TestModel _model;
public MainWindow()
{
InitializeComponent();
InitializeComponent();
_model = new TestModel();
this.DataContext = _model;
}
}
public enum MyLovelyEnum
{
FirstSelection,
TheOtherSelection,
YetAnotherOne
};
public class TestModel : DependencyObject
{
public MyLovelyEnum VeryLovelyEnum
{
get { return (MyLovelyEnum)GetValue(VeryLovelyEnumProperty); }
set { SetValue(VeryLovelyEnumProperty, value); }
}
public static readonly DependencyProperty VeryLovelyEnumProperty =
DependencyProperty.Register("VeryLovelyEnum", typeof(MyLovelyEnum), typeof(TestModel), new UIPropertyMetadata(0));
// from http://stackoverflow.com/questions/397556/wpf-how-to-bind-radiobuttons-to-an-enum
public class EnumBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object parameterValue = Enum.Parse(value.GetType(), parameterString);
return parameterValue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
return Enum.Parse(targetType, parameterString);
}
#endregion
}
}
The button & label is there from a previous test I did - I just left in...
The following declaration uses an integer instead of a default enum-value. Could be your instantiation problem...
public static readonly DependencyProperty VeryLovelyEnumProperty =
DependencyProperty.Register("VeryLovelyEnum", typeof(MyLovelyEnum), typeof(TestModel), new UIPropertyMetadata(0));
Try something like...
public static readonly DependencyProperty VeryLovelyEnumProperty =
DependencyProperty.Register("VeryLovelyEnum", typeof(MyLovelyEnum), typeof(TestModel), new UIPropertyMetadata(MyLovelyEnum.FirstSelection));

Categories

Resources