Binding ToggleButton IsChecked property to another ToggleButton not working - c#

I have two ToggleButtons.
I need to bind one IsChecked property to the other ToggleButton.
I use a custom Converter to inverse the value.
However, it's not working? Here's my code:
XAML:
<ToggleButton
IsChecked="{Binding Path=ToggleButton.IsChecked, ElementName=menuCatUit,
Converter={StaticResource InvertBool}}"/>
<ToggleButton x:Name="menuCatUit" IsChecked="True" />
Code:
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
}

Remove the "ToggleButton" from the path property.
You only need the property name.
<ToggleButton
IsChecked="{Binding Path=IsChecked, ElementName=menuCatUit,
Converter={StaticResource InvertBool}}"/>

Here is what i Do, I send them all to the same Checked and UnChecked event:
privateToggleButton Toggle;
public void ToggleButtonChecked(object sender, RoutedEventArgs e)
{
if (Toggle != null && Toggle != sender as ToggleButton)
{
Toggle.IsChecked = false;
}
Toggle = sender as ToggleButton;
}
public void ToggleButtonUnChecked(object sender, RoutedEventArgs e)
{
if (Toggle != null)
{
Toggle = null;
}
}
And then to check which ToggleButton is checked do something like this
switch (Toggle.Name)
{
case ("Button1"): /*MyCode*/ break;
};

Found the solution:
I needed to edit my converter because ToggleButton uses a nullable bool.
[ValueConversion(typeof(bool?), typeof(bool))]
public class InverseNullableBool : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool?))
throw new InvalidOperationException("The target must be a nullable boolean");
bool? b = (bool?)value;
return !(b.HasValue && b.Value);
}
}

Related

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

Bind two checboxes to a nullable bool property in MVVM

I try to connect two checkboxes to my ViewModel. Their behavior is like a radiobutton (exclusive) and TheeState. So both not checked or one of them checked
At the moment I am doing the job like that:
<dxlc:LayoutGroup>
<dxlc:LayoutItem Label="with errors">
<CheckBox IsChecked="{Binding OnlyMusicWithErrorsChecked}"></CheckBox>
</dxlc:LayoutItem>
<dxlc:LayoutItem Label="without errors">
<CheckBox IsChecked="{Binding OnlyMusicWithoutErrorsChecked}"></CheckBox>
</dxlc:LayoutItem>
</dxlc:LayoutGroup>
und ViewModel:
private bool _onlyMusicWithErrorsChecked;
public bool OnlyMusicWithErrorsChecked
{
get { return _onlyMusicWithErrorsChecked; }
set
{
SetProperty(ref _onlyMusicWithErrorsChecked, value, () => OnlyMusicWithErrorsChecked);
if (OnlyMusicWithErrorsChecked)
OnlyMusicWithoutErrorsChecked = false;
RaisePropertyChanged("AdditionalCriteriaHeader");
if (!_filteringData)
SelectData();
}
}
private bool _onlyMusicWithoutErrorsChecked;
public bool OnlyMusicWithoutErrorsChecked
{
get { return _onlyMusicWithoutErrorsChecked; }
set
{
SetProperty(ref _onlyMusicWithoutErrorsChecked, value, () => OnlyMusicWithoutErrorsChecked);
if (OnlyMusicWithoutErrorsChecked)
OnlyMusicWithErrorsChecked = false;
RaisePropertyChanged("AdditionalCriteriaHeader");
if (!_filteringData)
SelectData();
}
}
The question is: can I use only one property nullable bool to do this job?
You can bind both CheckBoxes to the same property OnlyMusicWithErrorsChecked, and in the second CheckBox add a converter that inverts the property's value:
<CheckBox IsChecked="{Binding OnlyMusicWithErrorsChecked, Converter={StaticResource Inverter}}"></CheckBox>
This converter would look somewhat like:
public class Inverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
return !((bool)value);
else // Fallback
return false;
}
}
EDIT: If you want to build a three-state-solution with only one bindable property, you'll need two converters (or one that can be parameterized):
public class MyConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty InvertProperty = DependencyProperty.Register(
"Invert", typeof (bool), typeof (MyConverter), new PropertyMetadata(default(bool)));
public bool Invert
{
get { return (bool) GetValue(InvertProperty); }
set { SetValue(InvertProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var val = (bool?) value;
switch (val)
{
case true:
return Invert;
break;
case false:
return !Invert;
break;
case null:
return false; // None of the checkboxes shall be active
break;
}
// Fallback
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var val = (bool)value;
switch (val)
{
case true:
return Invert;
break;
case false:
return null;
break;
}
// Fallback
return false;
}
}
The Invert property is set to false for the first checkbox, and true for the second one:
<Window.Resources>
<local:MyConverter x:Key="Converter" Invert="False"/>
<local:MyConverter x:Key="Inverter" Invert="True"/>
</Window.Resources>
Now you can use these two converter instances to bind the checkboxes to the same property:
<CheckBox IsChecked="{Binding MyProperty, Converter={StaticResource Converter}}" />
<CheckBox IsChecked="{Binding MyProperty, Converter={StaticResource Inverter}}" />
If the first box is checked the property will be false, if the second one is checked it will be true, and if no checkbox is checked it will be null.
However, I agree with ANewGuyInTown that you'd be better off with an Enum, since the bool types are a bit confusing here (by the way, most of the converter can be re-used when working with a three-state enum instead of nullable boolean).
Make a "NotConverter" on one of the checkboxes. Here's my implementation I've been using for a while in Windows Store and Phone apps. WPF is similar.
/// <summary>
/// Converts a bool to it's oppisite and back.
/// </summary>
public sealed class NotConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return (!(value is bool)) || !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return (value is bool) && (bool)value;
}
}
Register your converter in your App.xaml (or your view) file:
<Application.Resources>
<ResourceDictionary>
<converters:NotConverter x:Key="NotConverter"/>
</ResourceDictionary>
</Application.Resources>
Bind it in your view:
<CheckBox IsChecked="{Binding OnlyMusicWithErrorsChecked,Converter={StaticResource NotConverter}}"></CheckBox>
You could also name your other checkbox and bind to it's property like this:
<CheckBox x:Name="MyCheckBox" IsChecked="{Binding OnlyMusicWithErrorsChecked}"/>
<CheckBox IsChecked="{Binding ElementName=MyCheckBox,Path=IsChecked,Converter={StaticResource NotConverter}}"/>
Three state checkbox.
public bool CheckBox1
{
get { return _checkBox1; }
set
{
_checkBox1 = value;
if (value == true)
{
CheckBox2 = false;
}
OnPropertyChanged("CheckBox1");
}
}
private bool _checkBox2 = false;
public bool CheckBox2
{
get { return _checkBox2; }
set
{
_checkBox2 = value;
if (value == true)
{
CheckBox1 = false;
}
OnPropertyChanged("CheckBox2");
}
}
In Xaml Code something like this
<CheckBox Content="CheckBox1" IsChecked="{Binding CheckBox1, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Height="30" Width="100" />
<CheckBox Content="CheckBox2" IsChecked="{Binding CheckBox2, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Height="30" Width="100" />

Binding user control visibility to valueconverter

I do have a ValueConverter that works fine on elements e.g. like a STACKPANEL. The Method User.OwnsRight(Int32) returns true or false.
[ValueConversion(typeof(object), typeof(System.Windows.Visibility))]
public class ConverterUserRightVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bxSuite.Base.Objects.User locUser = (bxSuite.Base.Objects.User)value;
if (parameter == null) return System.Windows.Visibility.Visible;
if (locUser == null) return System.Windows.Visibility.Visible;
if (locUser.OwnsRight(System.Convert.ToInt32(parameter)))
{
return System.Windows.Visibility.Visible;
}
else
{
return System.Windows.Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
System.Windows.Visibility locVisibility = (System.Windows.Visibility)value;
if (locVisibility == System.Windows.Visibility.Hidden || locVisibility == System.Windows.Visibility.Collapsed)
{
return false;
}
else
{
return true;
}
}
}
So this XAML works great:
<StackPanel Visibility="{Binding Path=User, Converter={StaticResource ConverterUserRightVisibility}, ConverterParameter=4}"></StackPanel>
But if i do bind the Visibility of one of my UserControls to this Converter, the Converter doesn't even get fired. The UserControl itself works fine.
<my:MenuButtonLarge Visibility="{Binding Path=User, Converter={StaticResource ConverterUserRightVisibility}, ConverterParameter=4}"/>
Any ideas why? Any help appreciated! Thanks a lot.
EDIT: i've tried another Converter simply setting text on a Label control. Works fine, but if i use it on a Textproperty (dependency property) of a usercontrol the Converter with the ConverterParameter doesn't get fired? What am I missing?

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

Updating the source with multibinding

I have a combo box i.e editable. The combo box in my app acts like a editing control for all the datagrid cells i.e editing the value from the combo box should update the binding of my datagridtemplatecolumn. The below code updates the source if its a normal binding. If its a multibinding, it calls the convertback() function. I am using the below converter in order to update my source. The ParentID property is set to one way. I need to update only the ID property. Please help me with the convert back function
Xaml
<tk:Datagrid>
<tk:DataGridTemplateColumn Header="Event ID" MinWidth="100" CellTemplate="{StaticResource ClipsEventIDCellTemplate}" CellEditingTemplate="{StaticResource ClipsEventIDCellEditingTemplate}" />
</tk:Datagrid>
<DataTemplate x:Key="ClipsEventIDCellTemplate">
<TextBlock>
<TextBlock.Text>
<MultiBinding UpdateSourceTrigger="Explicit" Converter="{StaticResource EventIDConvert}" Mode="TwoWay" UpdateSourceTrigger="Explicit" >
<Binding Path="ParentID" Mode="OneWay"/>
<Binding Path="ID" Mode="TwoWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
<ComboBox x:Name="UniversalTextBox" IsEditable="True" ItemsSource="{Binding UniversalTextEntries, ElementName=TheMainWindow, Mode=OneWay}" KeyDown="OnUniversalTextKeyDown"/>
Code
// properties
public int ID
{
get { return m_id; }
set
{
if (m_id != value)
{
m_id = value;
NotifyPropertyChanged("ID");
}
}
}
public int ParentID
{
get;
set;
}
private void OnUniversalTextKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Enter && e.Key != Key.Escape)
return;
var comboBox = sender as ComboBox;
if (comboBox == null)
return;
BindingExpression binding = null;
MultiBindingExpression multibinding = null;
bool restoreGridFocus = false;
bool isMultibinding = false;
binding = comboBox.GetBindingExpression(ComboBox.TextProperty);
if (binding == null)
{
isMultibinding = true;
multibinding = BindingOperations.GetMultiBindingExpression(comboBox, ComboBox.TextProperty);
if (multibinding == null && multibinding.BindingExpressions.Count < 0)
return;
}
if (e.Key == Key.Escape)
{
restoreGridFocus = true;
if (!isMultibinding)
binding.UpdateTarget();
else
multibinding.UpdateTarget();
}
else if (e.Key == Key.Enter)
{
PopulateTextEntries(comboBox.Text);
restoreGridFocus = true;
if (!isMultibinding)
binding.UpdateSource();
else
multibinding.UpdateSource();
}
if (restoreGridFocus)// set the focus back to the lastfocuced cell in the datagrid
{
e.Handled = true;
if (m_BoundDataGrid != null)
{
var cell = m_BoundDataGridCell;
if (cell == null)
cell = DataGridUtils.GetCell(m_BoundDataGrid, m_BoundObject, m_BoundColumnIndex);
if (cell != null)
cell.Focus();
}
}
}
Converter
public class EventIDConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
return null;
return string.Format("{0}{1}", values[0], values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (value == null)
return null;
//ToDo
???????????????
}
#endregion
}
Create a Converter inherited from IMultiValueConverter.
Get the TextBlock.Texts value from the Convert method instead of the StringFormat and implement the ConvertBack to set the sources.
public class EventIDConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
return null;
return string.Format("{0} {1}", values[0], values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (value == null)
return null;
string[] splitValues = ((string)value).Split(' ');
return splitValues;
}
#endregion
}
Note:
I put a space to separate the two value. This is for the split method in the ConvertBack.
You set one of the bindings to OneWay

Categories

Resources