Can I write following code using STYLE in xaml?
cmbEnquiry.IsEnabled = (txtQuotationNo.IsEnabled && txtQuotationNo.IsReadOnly == false);
I'm not sure if this will work as is as I'm not in front of an IDE and am trying to code from memory, but if nothing else, it'll serve as an example for MultiBinding.
In your resources:
<local:AndNotConverter x:Key="AndNotConverter" />
<Style ...>
<Setter Property="IsEnabled">
<Setter.Value>
<MultiBinding Converter="{StaticResource AndNotConverter}">
<Binding ElementName="txtQuotationNo" Path="IsEnabled" />
<Binding ElementName="txtQuotationNo" Path="IsReadOnly" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style
In your code-behind:
public class AndNotConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return (bool)values[0] && !((bool)values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Edit:
Just verified the code, and it works as expected.
Related
I have converter for applying background color for DataGrid rows based on values in one column. It is working fine and is applying background color "on load". However I decided to dive deeper into WPF and MVVM and try to attach this event to CheckBox, but failed at this point. I am not getting any errors but my CheckBox does not work either. Any suggestions what should be edited? I guess I need to attach Convert event somehow to BacgroundColorBool?
IDToBackgroundConverter.cs:
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace Inspector_FilterTest
{
class IDToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string)
{
if (value.ToString().Trim().StartsWith("7")) return Brushes.DarkSlateBlue;
if (value.ToString().Trim().StartsWith("6")) return Brushes.Peru;
}
// value is not an integer. Do not throw an exception
// in the converter, but return something that is obviously wrong
return Brushes.Firebrick;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I use it in XAML:
<Window.Resources>
<Inspector_FilterTest:IDToBackgroundConverter x:Key="IDToBackgroundConverter"/>
</Window.Resources>
...
<DataGrid x:Name="DataGrid1" ItemsSource="{Binding MainDataTable}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" >
<Setter Property="Background" Value="{Binding YRNRO, Converter={StaticResource IDToBackgroundConverter}}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
This is working fine and background colors are set (based on starting number in YRNRO) when I am loading my DataGrid. However I would like to be able to control this with CheckBox.
I have created a CheckBox:
ViewModel_Main.cs:
// Binding checkbox background color
private bool _BacgroundColorBool;
public bool BacgroundColorBool
{
get => this._BacgroundColorBool;
set
{
this._BacgroundColorBool = value;
OnPropertyChanged();
// Refresh the DataTable filter expression
EnableRowFiltering();
}
}
XAML:
<CheckBox Style="{StaticResource MyCheckBox}" IsChecked="{Binding BacgroundColorBool}" x:Name="ActiveCustomer_Copy" Content="" HorizontalAlignment="Left" Margin="221,55,0,0" VerticalAlignment="Top"/>
but I don't understand how to connect this all together in order to be able to control "background fill applied"/"background fill not applied" with CheckBox.
EDIT:
Some corrections done to code originally provided by BionicCode (no errors in debugger anymore, I hope this is correct?). In order to get color back to transparent in Datagrid after CheckBox is unchecked pay attention to return Brushes.Transparent;. That is where you can apply "revertBack" logic.
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace Liinos_inspector_FilterTest
{
class IDToBackgroundConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// The order of the parameters in the 'values' array is equal to the order
// of the input binding as they are defined in the MultiBinding
var isBackgroundEnabled = (bool)values[0];
if (!isBackgroundEnabled)
{
return Brushes.Transparent;
}
if (values[1] is string stringValue)
{
return stringValue.Trim().StartsWith("7")
? Brushes.DarkSlateBlue
: stringValue.Trim().StartsWith("6")
? Brushes.Peru
: Brushes.Firebrick;
}
return Binding.DoNothing;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
One solution, that doesn't require modification of your view model, is to use a MultiBinding with a IMultiValueConverter. A IMultiValueConverter accepts multiple inputs returned from a MultiBinding, to convert them to a single output (and reverse):
First, turn the IValueConverter into a IMultiValueConverter.
It's best practice to throw the exception that describes the cause of the error the best.
The NotImplementedException is primarily intended to notify the developer that he forgot to implement a relevant and referenced method. It's like a 'TODO'. Since you deliberately decided not to implement the ConvertBack member, because you decided not to support the backwards conversion, you should throw a NotSupportedException instead. This notifies the consumer of your type that the method is not supported instead of implying that the author simply forgot to implement it.
class IDToBackgroundConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// The order of the parameters in the 'values' array is equal to the order
// of the input binding as they are defined in the MultiBinding
var isBackgroundEnabled = (bool) values[0];
if (!isBackgroundEnabled)
{
return Brushes.Transparent;
}
if (values[1] is string stringValue)
{
return stringValue.Trim().StartsWith("7")
? Brushes.DarkSlateBlue
: stringValue.Trim().StartsWith("6")
? Brushes.Peru
: Brushes.Firebrick;
}
return Binding.DoNothing;
}
// Throw a NotSupportedException instead of a NotImplementedException
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
=> throw new NotSupportedException();
}
Setup the MultiBinding:
<CheckBox IsChecked="{Binding BackgroundColorBool}" />
<DataGrid>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background">
<Setter.Value>
<MultiBinding>
<MultiBinding.Converter>
<IDToBackgroundConverter />
</MultiBinding.Converter>
<Binding Path="BackgroundColorBool" />
<Binding Path="YRNRO" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Because the purpose of the logic is to handle the coloring or visuals of the view, you should not implement this logic or store related data in the view model.
View and view model should never mix!
Whether to use the previous example or to eliminate the BackgroundColorBool property in the view model, depends on the the purpose of the CheckBox. Its name suggests a data related purpose ("ActiveCustomer...") and of course could bind to the view model.
But the name of the source property ("BackgroundColor..."), the CheckBox binds to, suggests a merely view related purpose. In this case the property should not be in the view model. Instead move it as a DependencyProperty to the hosting view or bind the CheckBox directly:
<CheckBox x:Name="ActiveCustomer_Copy" />
<DataGrid>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background">
<Setter.Value>
<MultiBinding>
<MultiBinding.Converter>
<IDToBackgroundConverter />
</MultiBinding.Converter>
<Binding ElementName="ActiveCustomer_Copy"
Path="IsChecked" />
<Binding Path="YRNRO" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
I need to change value of a property on a trigger, and I need to do it according to the existing value with some parameter. So, I've tried to use xaml like this:
<Style.Triggers>
<Trigger ...>
<Setter Property="BorderBrush"
Value="{Binding Path=BorderBrush,
RelativeSource={RelativeSource Self},
Converter={StaticResource MyConverter},
ConverterParameter=ParamValue}" />
</Trigger>
</Style.Triggers>
And converter is simple:
[ValueConversion(typeof(SolidColorBrush), typeof(SolidColorBrush))]
public class MyConverter : IValueConverter
{
public object Convert(object existingValue, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//change the brush
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
But with such code existingValue parameter in MyConverter.Convert() is null.
So, is there a way to apply converter to a property using its own value? I can't pass it as ConverterParameter because that is used for another data needed by the converter.
My program has this kind of a multi bind
<MultiBinding Converter="{StaticResource myConverter}" Mode="OneWay">
<Binding Path="SelectedItems.Count"/>
<Binding Path="EffectiveStyleContext.Selection"/>
</MultiBinding>
IS there anyway to get the current enable disable status in the Convert method
class myConverter: IMultiValueConverter
{
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//I need to get current status here
}
}
You have to pass control itself into a ValueConverter.
Your modified Xaml will be
<MultiBinding Converter="{StaticResource myConverter}" Mode="OneWay">
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding Path="SelectedItems.Count"/>
<Binding Path="EffectiveStyleContext.Selection"/>
</MultiBinding>
Now in your coverter code you will be able to access control.
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var control = values[0] as FrameworkElement;
var value1 = values[1] as int;
// write your logic here.
}
public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return throw new System.NotImplementedException();;
}
}
You can also bind direct to the property itself:
<Binding Path="IsEnabled" RelativeSource="{RelativeSource Self}" />
I would actually argue that this is preferable because the MultiBinding won't get updated when the enable state changes otherwise.
I have a multiBinding which works great,
I want to be able to sort a certain column, it displays w:width, h:heightAs far as I understand I need to build a custom IComparer class which will to the comparison.
Here is my XAML
<igWPF:UnboundField Label="Output
Width/Height" Width="auto">
<igWPF:Field.Settings>
<igWPF:FieldSettings SortComparer="{StaticResource SortWidthHeightComparer }">
<igWPF:FieldSettings.CellValuePresenterStyle>
<Style TargetType="{x:Type igWPF:CellValuePresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type igWPF:CellValuePresenter}" >
<TextBlock Margin="3">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource settingsBdsToStringConverter}">
<Binding Path="DataItem.Key"/>
<Binding Path="DataContext.SelectedPipeMode" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type igWPF:XamDataGrid}}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</igWPF:FieldSettings.CellValuePresenterStyle>
</igWPF:FieldSettings>
</igWPF:Field.Settings>
</igWPF:UnboundField>
Here is my multibinding converter
class SettingsOutputResToStringConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[1] is Mode && values[0] is ConfigurationKey)
{
var pMode = (Mode)values[1];
var key = values[0] as ConfigurationKey;
var res = key.GetOutput(pMode);
return String.Format("W: {0}, H: {1}", res.Width, res.Height);
}
return String.Empty;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
However my problem is how to I pass the multibind result into the Comparer class
class SortWidthHeightComparer : IComparer
{
public int Compare(object x, object y)
{
return 1;
}
}
object x and object y , are always null
Gilad,
I found this post:
http://www.infragistics.com/community/forums/t/17878.aspx
It seems like a simple solution.
I'm going to try the same approach for filtering records.
I created a simple Converter to concatenate the text of four TextBoxes in my WPF app.
Here is the Converter:
public class FourString:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return string.Format("{0}{1}{2}{3}", values[0], values[1], values[2], values[3]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new object[] { };
}
}
in Xaml I use this code:
<local:FourString x:Key="converter"/>
<TextBox Grid.ColumnSpan="4" Margin="95,7.5,71.25,3.75" Name="CodeBoatTxt" >
<TextBox.Text>
<MultiBinding Converter="{StaticResource converter}" >
<Binding ElementName="CountryStringaTxt" Path="Text" />
<Binding ElementName="CityStringaTxt" Path="Text" />
<Binding ElementName="ServiceStringaTxt" Path="Text" />
<Binding ElementName="DurationStringaTxt" Path="Text" />
</MultiBinding>
</TextBox.Text>
</TextBox>
When in debug, this error appears in the CodeBoatTxt textbox: "DependecyProperty.UnsetValue".
What is wrong with my converter?
DependencyProperty.UnsetValue is passed into the converter when a Binding is valid, but does not have its value set yet. I would check the Bindings comprising your MultiBinding in isolation and ensure they are correct.