Converter not hooked to PropertyChanged event - c#

I have this treeview with an hierarchy binded to an entity framework model.
The rectangle below works only for the initial load. And does not update its color etc for when I fire the Propertychanged event.
<HierarchicalDataTemplate ItemsSource="{Binding tblLines}">
<StackPanel Orientation="Horizontal" Margin="2">
<Image Height="15" Margin="4" x:Name="imgTreeProject" Source="/DES STUDIO PR;component/Resources/Images/folder_closed.png"/>
<TextBlock Text="{Binding Name}" Margin ="8" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Rectangle Name="X" Width="10" Height="10" Fill="{Binding Path=.,Converter={StaticResource CheckoutConverter},UpdateSourceTrigger=PropertyChanged}" ToolTip="{Binding Path=.,Converter={StaticResource CheckoutPersonConverter},UpdateSourceTrigger=PropertyChanged}" ToolTipService.IsEnabled="{Binding Path=.,Converter={StaticResource CheckoutToolTipVis},UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
Here are my converters:
public class CheckoutConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is TblBase)
{
var Baseentity = value as TblBase;
if (Baseentity.COID == MainWindow.LocalUser.ID)
{
return Brushes.Green;
}
else if ((Baseentity.COID == 0) || (Baseentity.COID == null))
{
return Brushes.Transparent;
}
else if (Baseentity.COID != 0)
{
return Brushes.Black;
}
}
return Brushes.Black;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class CheckoutPersonConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is TblBase)
{
var Baseentity = value as TblBase;
//COID equals other user's ID
if ((Baseentity.COID != 0) && (Baseentity.COID != null) && (Baseentity.COID != MainWindow.LocalUser.ID))
{
var user = DESDatabase.GetUser(Baseentity.COID.Value);
return "Checked out by " + user;
}
else if (Baseentity.COID == MainWindow.LocalUser.ID)
return "You have this node checked out.";
else
return "error";
}
return "error";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class CheckoutToolTipVis : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is TblBase)
{
var Baseentity = value as TblBase;
//COID equals other user's ID
if ((Baseentity.COID != 0) && (Baseentity.COID != null))
{
return true;
}
else
return false;
}
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And my property changed event does get called here:
private Nullable<int> _coid;
public Nullable<int> COID
{
get
{
NotifyPropertyChanged();
return _coid;
}
set
{
_coid = value;
NotifyPropertyChanged();
}
}
The property changed events work for treeview bindings that do not use converters, therefore I am not sure what I am missing.

converters (IValueConverter) don't subscribe to PropertyChanged event, that's binding's responsibility.
currenlty the converter is used with binding to entire object, not to property:
Fill="{Binding Path=.,Converter={StaticResource CheckoutConverter},UpdateSourceTrigger=PropertyChanged}"
so th updates of COID happen unnoticed.
you need to change bidning to
Fill="{Binding Path=COID,Converter={StaticResource CheckoutConverter},UpdateSourceTrigger=PropertyChanged}"
and change converter as well:
public class CheckoutConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is int?)
{
var COID = value as int?;
if (COID == MainWindow.LocalUser.ID)
{
return Brushes.Green;
}
else if ((COID == 0) || (COID == null))
{
return Brushes.Transparent;
}
else if (COID != 0)
{
return Brushes.Black;
}
}
return Brushes.Black;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

IntToVisibilityConverter In WPF

I have a StackPanel and Border and what I need to achieve is whenever the Children.Count property of the panel is 0 the Border become visible. I tried to many times but don't know what's wrong with my code!?
public class IntToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value is int integer && integer == 0) ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Xaml
<local:IntToVisibilityConverter x:Key="IntConverter"/>
<Border Visibility="{Binding ElementName=MainPagesStackPanel, Path=Children.Count, Converter={StaticResource IntConverter}}"/>

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");
}

How to bind 2 textboxes to one property?

I have a propery PhoneNumber and in the UI, I have 2 textboxes, one is the prefix, and the other one is the postfix, how can I bind it to the property? (The property inside the DataContext).
<TextBox Grid.Column="0" MaxLength="3" /> //Prefix
<TextBlock Grid.Column="1" Text="-" />
<TextBox Grid.Column="2" /> //Postfix
The only way I see it work is with code behind using textbox1.Text + textbox2.Text... Is there a better way?
Thanks in advance :)
Just use two more properties in the data context
code is not complied or tested
public string PhoneNumber { get; set; }
public string Prefix
{
get
{
return PhoneNumber.Substring(0, 3);
}
set
{
// replace the first three chars of PhoneNumber
PhoneNumber = value + PhoneNumber.Substring(3);
}
}
public string Postfix
{
get
{
return PhoneNumber.Substring(3);
}
set
{
// replace the chars of starting from index 3 of PhoneNumber
PhoneNumber = PhoneNumber.Substring(0, 3) + value;
}
}
I think uou can use Converter for this purpose, the example going one way can look like this:
In this my Number is a string 000-000000, but you can surely change it.
In XAML:
<Window.Resources>
<conv:PostixConverter x:Key="PostfixConv" xmlns:conv="clr-namespace:Example.Converters"/>
<conv:PrefixConverter x:Key="PrefixConv" xmlns:conv="clr-namespace:Example.Converters"/>
</Window.Resources>
<StackPanel>
<TextBox MaxLength="3" Text="{Binding Number, Converter={StaticResource PrefixConv}}"/>
<TextBlock Text="-" />
<TextBox Text="{Binding Number, Converter={StaticResource PostfixConv}}"/>
</StackPanel>
And in code behind:
namespace Example.Converters
{
public class PrefixConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return null;
else return ((string)value).Substring(0, 3);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class PostixConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return null;
else return ((string)value).Substring(4);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

DataTemplate and WrapPanel visibility

I'm getting this error:
Provide value on 'System.Windows.Markup.StaticResourceHolder' threw an
exception.
With xaml code:
<WrapPanel Orientation="Horizontal" Grid.Row="0" >
<WrapPanel.Visibility>
<Binding Path="setVisible" Converter="{StaticResource BooleanToVisibilityConverter}" ConverterParameter="{Binding setVisible}"/>
</WrapPanel.Visibility>
//textblocks goes here
</WrapPanel>
and class:
public class dataTemplate_xItem
{
(...)
public bool setVisible { get; set; }
public sealed class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var flag = false;
if (value is bool)
{
flag = (bool)value;
}
else if (value is bool?)
{
var nullable = (bool?)value;
flag = nullable.GetValueOrDefault();
}
if (parameter != null)
{
if (bool.Parse((string)parameter))
{
flag = !flag;
}
}
if (flag)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var back = ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
if (parameter != null)
{
if ((bool)parameter)
{
back = !back;
}
}
return back;
}
}
}
And before i'm adding item to ListView, checking
if(myValue != 0)
newItem.setVisible = true;
else
newItem.setVisible = false;
Any idea what goes wrong? :)
icebat is correct. The ConverterParameter is not a DependencyProperty and therefore, cannot be bound to. Looking at your xaml, you do not need the ConverterParameter. Nor do you need the extended markup for the binding expression. You xaml can simply be
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="Boolean2Visibility" />
</UserControl.Resources>
<WrapPanel Orientation="Horizontal" Grid.Row="0" Visibility="{Binding Path=setVisible, Converter={StaticResource Boolean2Visibility}}" />
This code assumes you are in a UserControl

C# WPF IsEnabled using multiple bindings?

I have a WPF xaml file describing a section of a GUI and I'd like the enabling/disabling of a particular control to be dependent on two others. The code looks something like this at the moment:
<ComboBox Name="MyComboBox"
IsEnabled="{Binding ElementName=SomeCheckBox, Path=IsChecked}"/>
But I'd like it to be dependant on another checkbox as well so something like:
<ComboBox Name="MyComboBox"
IsEnabled="{Binding ElementName=SomeCheckBox&AnotherCheckbox, Path=IsChecked}"/>
What's the best way to go about that? I can't help feeling I'm missing something obvious or going about this the wrong way?
You can use a MultiBinding with a converter which implements IMultiValueConverter.
Just to give an answer you can (almost) copy&paste:
Static resource needed:
<converterNamespace:BooleanAndConverter x:Key="booleanAndConverter" />
The ComboBox:
<ComboBox Name="MyComboBox">
<ComboBox.IsEnabled>
<MultiBinding Converter="{StaticResource booleanAndConverter}">
<Binding ElementName="SomeCheckBox" Path="IsChecked" />
<Binding ElementName="AnotherCheckbox" Path="IsChecked" />
</MultiBinding>
</ComboBox.IsEnabled>
</ComboBox>
The code for the converter:
namespace ConverterNamespace
{
public class BooleanAndConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
foreach (object value in values)
{
if ((value is bool) && (bool)value == false)
{
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("BooleanAndConverter is a OneWay converter.");
}
}
}
You can also try shorter version of the same:
public class BooleanAndConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values.OfType<IConvertible>().All(System.Convert.ToBoolean);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
public class BooleanOrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values.OfType<IConvertible>().Any(System.Convert.ToBoolean);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
and, of course, you may need the converters for visibility, too:
public class BooleanOrToVisibilityConverter : IMultiValueConverter
{
public Visibility HiddenVisibility { get; set; }
public bool IsInverted { get; set; }
public BooleanOrToVisibilityConverter()
{
HiddenVisibility = Visibility.Collapsed;
IsInverted = false;
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
bool flag = values.OfType<IConvertible>().Any(System.Convert.ToBoolean);
if (IsInverted) flag = !flag;
return flag ? Visibility.Visible : HiddenVisibility;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanAndToVisibilityConverter : IMultiValueConverter
{
public Visibility HiddenVisibility { get; set; }
public bool IsInverted { get; set; }
public BooleanAndToVisibilityConverter()
{
HiddenVisibility = Visibility.Collapsed;
IsInverted = false;
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
bool flag = values.OfType<IConvertible>().All(System.Convert.ToBoolean);
if (IsInverted) flag = !flag;
return flag ? Visibility.Visible : HiddenVisibility;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I believe you may have to use a MultiBinding with a MultiValueConverter. See here: http://www.developingfor.net/wpf/multibinding-in-wpf.html
Here is a directly related example: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5b9cd042-cacb-4aaa-9e17-2d615c44ee22
As extension to qqbenq's answer:
Added the function to handle the Count of a Collection for example if you want to check if some item of a ListView is selected.
Converter:
public class IsEnabledConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (var value in values)
{
switch (value)
{
case bool b when !b:
case int i when i == 0:
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
Namespace <theNamespace:IsEnabledConverter x:Key="IsEnabledConverter"/>
Button
<Button x:Name="MyButton">
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource IsEnabledConverter}">
<Binding ElementName="MyListView" Path="SelectedItems.Count"/>
<Binding ElementName="MyCheckBox" Path="IsChecked"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
When you don't want to use MultiBinding
public class AndEnabledTextBox : TextBox
{
public static readonly DependencyProperty AndEnabled1SubProperty =
DependencyProperty.Register(nameof(AndEnabled1), typeof(bool), typeof(AndEnabledTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnAndEnabledPropertyChanged)));
public static readonly DependencyProperty AndEnabled2SubProperty =
DependencyProperty.Register(nameof(AndEnabled2), typeof(bool), typeof(AndEnabledTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnAndEnabledPropertyChanged)));
public bool AndEnabled1 { get { return (bool)GetValue(AndEnabled1SubProperty); } set { SetValue(AndEnabled1SubProperty, value); } }
public bool AndEnabled2 { get { return (bool)GetValue(AndEnabled2SubProperty); } set { SetValue(AndEnabled2SubProperty, value); } }
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
IsEnabled = AndEnabled1 && AndEnabled2;
}
protected static void OnAndEnabledPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
AndEnabledTextBox textBox = (AndEnabledTextBox)obj;
textBox.IsEnabled = textBox.AndEnabled1 && textBox.AndEnabled2;
}
}
XMAL is more simplified when you use inherited controls.
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1">
<StackPanel>
<ToggleButton x:Name="switch1" Content="{Binding ElementName=switch1, Path=IsChecked}"/>
<ToggleButton x:Name="switch2" Content="{Binding ElementName=switch2, Path=IsChecked}"/>
<local:AndEnabledTextBox Text="TEXT"
AndEnabled1="{Binding ElementName=switch1, Path=IsChecked, Mode=OneWay}"
AndEnabled2="{Binding ElementName=switch2, Path=IsChecked, Mode=OneWay}"/>
</StackPanel>
</Window>

Categories

Resources