Updating the source with multibinding - c#

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

Related

Converter not hooked to PropertyChanged event

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

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

Hide NewItemPlaceHolder in ListBox WPF

I have a problem with DataGrid and ListBox. Users can add new rows to datagrid, but an empty row is visible in listbox.
I found info about NewItemPlaceHolder, but I don't know how to hide it in listbox.
XAML:
<ListBox AlternationCount="2"
ItemContainerStyle="{StaticResource alternatingWithTriggers}"
x:Name="ViewListBox"
Background="AliceBlue"
ItemsSource="{Binding Converter={StaticResource IgnoreNewItemPlaceholderConverter}}" >
CS:
public static readonly IgnoreNewItemPlaceholderConverter Instance
= new IgnoreNewItemPlaceholderConverter();
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (value != null && value.ToString() == "{NewItemPlaceholder}")
{
return DependencyProperty.UnsetValue;
}
MessageBox.Show(value.ToString());
return value;
}
Create a new collection view (SelectsView) of Selects (ObservableCollection of SelectItem) and filter the view
SelectsView = new();
SelectsView.Source = Selects;
SelectsView.Filter += SelectsView_Filter;
SelectsListView.ItemsSource = SelectsView.View;
The view's filter
private void SelectsView_Filter(object sender, FilterEventArgs e)
{
// Hide NewItemPlaceHolder
if (e.Item is SelectItem)
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}

Binding ToggleButton IsChecked property to another ToggleButton not working

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

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

Categories

Resources