I have a WPF control that has a Message property.
I currently have this:
<dxlc:LayoutItem >
<local:Indicator Message="{Binding PropertyOne}" />
</dxlc:LayoutItem>
But i need that Message property to be bound to two properties.
Obviously can't be done like this, but this can help explain what it is I want:
<dxlc:LayoutItem >
<local:Indicator Message="{Binding PropertyOne && Binding PropertyTwo}" />
</dxlc:LayoutItem>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
Try use the MultiBinding:
Describes a collection of Binding objects attached to a single binding target property.
Example:
XAML
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myNameConverter}"
ConverterParameter="FormatLastFirst">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Converter
public class NameConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string name;
switch ((string)parameter)
{
case "FormatLastFirst":
name = values[1] + ", " + values[0];
break;
case "FormatNormal":
default:
name = values[0] + " " + values[1];
break;
}
return name;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
string[] splitValues = ((string)value).Split(' ');
return splitValues;
}
}
You can't do And operation in XAML.
Create wrapper property in your view model class which will return and of two properties and bind with that property instead.
public bool UnionWrapperProperty
{
get
{
return PropertyOne && PropertyTwo;
}
}
XAML
<local:Indicator Message="{Binding UnionWrapperProperty}" />
Another approach would be to use MultiValueConverter. Pass two properties to it and return And value from the converter instead.
Related
I am trying to use a single IMultiValueConverter for more than one controls in XAML.
I am using a simple string Literal to tell what value the IMultiValueConverter is supposed to return.
But I am getting DependencyProperty.UnsetValue in values[2],ie value of parametter named Command when it comes to convert function of ModifierCategoryEnableDisable.
Similar arrangment is working on similar controls on this XAML form within other IMultiValueConverters but not here.
Please guide what am i missing?
NOTE:
CurrentRec is the currently selected object from the ViewModel
DM_CategoryData is a Class and Current_Selected_Category is a List<DM_CategoryData> in the ViewModel's current object,ie CurrenRec.
XAML:
<GroupBox Width="226" Height="117" Margin="0" Canvas.Top="252" Header="Modifiers" Canvas.Left="55" >
<GroupBox.IsEnabled>
<MultiBinding Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" Converter="{StaticResource MDNS}">
<Binding Path="SearchFound" />
<Binding Path="CurrentRec.Current_Selected_Category"/>
<Binding Path="Command" FallbackValue="1" />
</MultiBinding>
</GroupBox.IsEnabled>
</GroupBox>
C#:
public class ModifierCategoryEnableDisable : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string Command = values[2].ToString();
bool Retval1 = false;
string Retval2 = "";
switch(Command)
{
case "1":
bool SearchFound = (bool)values[0];
DM_CategoryData CurrentSelectedItemCategory = (DM_CategoryData)(values[1]);
Retval1 = SearchFound && (CurrentSelectedItemCategory == null ? true : CurrentSelectedItemCategory.IsModifier.Equals("1") ? false : true);
break;
case "2":
Retval2 = "0";
break;
}
if(Command.Equals("1"))
{
return Retval1;
}
else
{
return Retval2;
}
}
}
In order to provide additional static data to a multibinding converter, use a ConverterParameter:
<MultiBinding Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" Converter="{StaticResource MDNS}" ConverterParameter="1">
<Binding Path="SearchFound" />
<Binding Path="CurrentRec.Current_Selected_Category"/>
</MultiBinding>
And check the parameter in the Convert method:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string Command = parameter as string;
// ...
}
You are trying to set the fallbackvalue for GroupBox.IsEnabled property and it is a bool type. But you are setting the value as 1. So only Values[2] returns the UnsetValue. Try to set bool value as Fallbackvalue.
Can I pass an index number into this list (SomeList)?
FontSize="{Binding FontSize, Source={x:Static ut:ViewSetupData.SomeList}, FallbackValue=12}"
You can put a constant indexer in the Path:
{Binding Path=[(sys:Int32)0], Source={x:Static ut:ViewSetupData.SomeList}}
But you can't bind a property of a Binding, so there's no way to stuff a parameter in there. However, you can combine multiple bindings in a MultiBinding, so you could use one of those with a multi-value converter:
C#:
public class IListIndexerConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// You might want a little more error-checking than this...
return ((IList)values[0])[(int)values[1]];
}
public virtual object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
XAML:
<TextBlock>
<TextBlock.Resources>
<local:IListIndexerConverter x:Key="ListIndexer" />
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource ListIndexer}">
<Binding Source="{x:Static ut:ViewSetupData.SomeList}" />
<Binding
ElementName="MyComboBox"
Path="SelectedIndex"
/>
</MultiBinding>
</TextBlock.Test>
</TextBlock>
Update
While you were marking this as the solution, I was writing a more complete solution that addressed your need to grab a property from the list item:
C#:
public class ListItemPropertyGetter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
try
{
var list = values[0] as IList;
var index = (int)(values[1] ?? 0);
var propname = values[2] as String;
object item = list[index];
var prop = item.GetType().GetProperty(propname);
var propvalue = prop.GetValue(item);
return propvalue;
}
catch
{
return null;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
// Gotta put these somewhere
public static List<FontSizeThing> FontSizeThings { get; } =
new List<FontSizeThing>
{
new FontSizeThing(10),
new FontSizeThing(10.5),
new FontSizeThing(11),
new FontSizeThing(12),
new FontSizeThing(14),
new FontSizeThing(15),
};
}
public class FontSizeThing {
public FontSizeThing(double n) { FontSize = n; }
public double FontSize { get; set; }
}
XAML:
<ComboBox x:Name="FontSizeOptionCombo">
<sys:Int32>0</sys:Int32>
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
<sys:Int32>3</sys:Int32>
<sys:Int32>4</sys:Int32>
</ComboBox>
<TextBlock Text="Testing">
<TextBlock.Resources>
<hconv:ListItemPropertyGetter x:Key="ListItemPropertyGetter" />
</TextBlock.Resources>
<TextBlock.FontSize>
<MultiBinding Converter="{StaticResource ListItemPropertyGetter}" StringFormat="{}{0}">
<Binding Source="{x:Static hconv:ListItemPropertyGetter.FontSizeThings}" />
<Binding ElementName="FontSizeOptionCombo" Path="SelectedItem" />
<Binding Source="FontSize" />
</MultiBinding>
</TextBlock.FontSize>
</TextBlock>
FINAL UPDATE
Note that if I had merely populated FontSizeOptionCombo with the FontThings themselves, I could very simply have bound like this:
<ComboBox
x:Name="OtherCombo"
ItemsSource="{x:Static hconv:ListItemPropertyGetter.FontSizeThings}"
DisplayMemberPath="FontSize"
FontSize="{Binding SelectedItem.FontSize, ElementName=OtherCombo, FallbackValue=20}"
/>
If that fits in with what you're doing, it's by far the nicest way.
My application is having 5 text boxes and I want content of them in my ExecuteInsert function.
right now my Button contains following binding.
<Button
Content="Add"
HorizontalAlignment="Left"
Margin="22,281,0,0"
VerticalAlignment="Top"
Width="75"
Command="{Binding Add}"
CommandParameter="{Binding ElementName=txtname}"
RenderTransformOrigin="1.023,0.765"/>
And my ExecuteInsert function is as follows. I just want to pass multiple command
parameters means(multibinding) can anybody help??
private void ExecuteInsert(object obj)
{
TextBox textbox = obj as TextBox;
try
{
ExecuteConnect(obj);
oleDbCommand.CommandText = "INSERT INTO emp(FirstName)VALUES ('" + textbox.Text + "')";
oleDbCommand.ExecuteNonQuery();
MessageBox.Show("Data Saved");
}
catch (Exception ex)
{
MessageBox.Show("ERROR" + ex);
}
}
You will have to create the Multivalueconveter for this:
Xaml:
converter:
<local:MyConverter x:Key="myConverter" />
Button:
<Button>
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource myConverter}">
<Binding Path="" ElementName=""/>
<Binding Path=""/>
<Binding Path=""/>
</MultiBinding>
</Button.CommandParameter>
</Button>
C#
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
you will get the object array in the command handler.
Thanks
Your question is not exactly clear, but i would think the simplest way to pass the content of your 5 textboxes to your ExecuteInsert function is to bind each of those textboxes to a property in your viewmodel class and use those properties in your function...
I have a MultiBinding that is not working on TextBox.Text. I have the same code that is binding properly to Value of Extended WPF Toolkit's IntegerUpDown.
It is going through an IMultiValueConverter that takes the bound POCO and the listbox it is part of (it is displaying the order of the item in the listbox)
Here is the code:
<!--works-->
<wpf:IntegerUpDown ValueChanged="orderChanged" x:Name="tripOrder">
<wpf:IntegerUpDown.Value>
<MultiBinding Converter="{StaticResource listBoxIndexConverter}" Mode="OneWay">
<Binding />
<Binding ElementName="listTrips" />
</MultiBinding>
</wpf:IntegerUpDown.Value>
</wpf:IntegerUpDown>
<!--doesn't work-->
<TextBox x:Name="tripOrder2">
<TextBox.Text>
<MultiBinding Converter="{StaticResource listBoxIndexConverter}" Mode="OneWay">
<Binding />
<Binding ElementName="listTrips" />
</MultiBinding>
</TextBox.Text>
</TextBox>
Here is the result:
I don't believe it is relevant, but just in case, here is the class that performs the conversion:
public class ListBoxIndexConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var trip = values[0] as TripBase;
if (trip == null)
{
return null;
}
var lb = values[1] as CheckListBox;
if (lb == null)
{
return null;
}
//make it 1 based
return lb.Items.IndexOf(trip) + 1;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
The converter should return the type that the property expects. The reason is that in regular use of the properties (i.e. without Binding), the properties may have type converters that convert from one type (or more) to the type required by the property. For example, when you write:
<ColumnDefinition Width="Auto"/>
there's a converter that converts string "Auto" to:
new GridLength(1, GridUnitType.Auto)
When using binding, this mechanism is bypassed since the converter should return the right type.
So, to fix your issue, at the return of your converter:
return (lb.Items.IndexOf(trip) + 1).ToString();
This should fix the TextBox.
Now, for the IntegerUpDown. It sounds like it actually expects to receive an int and returning a string will break it. So, again, change the return of the converter:
if (targetType == typeof(int))
{
return lb.Items.IndexOf(trip) + 1;
}
else if (targetType == typeof(string))
{
return (lb.Items.IndexOf(trip) + 1).ToString();
}
else
{
throw new NotImplementedException(String.Format("Can not convert to type {0}", targetType.ToString()));
}
The binding is not going to work, because the listTrips is not changing when the list box's selected value changes. The thing that changes is listTrips.SelectedItem, so you should bind against it:
<Binding Path="SelectedItem" ElementName="listTrips"/>
Actually, I wonder why it works for the first example.
Im using Bindings to change Style on a button. To be able to do this Ive found that I need to use MultipleBinding. But for some reason this doesnt work.
Here is the code in my UserControl (which inherits from a uc-base-class):
<CtrlLib:UcBaseCounter
...
x:Name="mySelf">
<CtrlLib:UcBaseCounter.Resources>
<ResourceDictionary>
...
<CtrlLib:StyleConverter x:Key="styleConverter" />
</ResourceDictionary>
</CtrlLib:UcBaseCounter.Resources>
<Grid Margin="2">
<Button Click="Ctrl_Click">
<Button.Style>
<MultiBinding Converter="{StaticResource styleConverter}">
<MultiBinding.Bindings>
<Binding ElementName="mySelf"/>
<Binding Path="HealthStatus"/>
</MultiBinding.Bindings>
</MultiBinding>
</Button.Style>
But this works great:
<TextBlock Text="{Binding ElementName=mySelf, Path=HealthStatus}" />
I get this warning in the Output window during runtime:
System.Windows.Data Warning: 40 : BindingExpression path error: 'HealthStatus' property not found on 'object' ''MyLayoutViewModel' (HashCode=3696098)'. BindingExpression:Path=HealthStatus; DataItem='MyLayoutViewModel' (HashCode=3696098); target element is 'Button' (Name='Component'); target property is 'Style' (type 'Style')
Why cant HealthStatus be found in the MultipleBinding when it is obviously there?
I dont include the code for the converter since it doesnt seem to be the problem. HealthStatus is declared as enum but since it is working for the "singlebinding" and the error-message only says it cant find it I dont think thats the problem either.
Thanx!
Added:
This is my Converter:
public class StyleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
FrameworkElement targetElement = values[0] as FrameworkElement;
HealthStatus.Statuses status;
status = ((UcBase)targetElement).HealthStatus;
Style newStyle;
switch (status)
{
case HealthStatus.Statuses.Error:
newStyle = (Style)targetElement.TryFindResource("RedStyle");
return newStyle;
...
}
return (Style)targetElement.TryFindResource("WhiteStyle");
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
and the styles are declared in a resourcedictionary:
<Style x:Key="WhiteStyle" TargetType="Button">
HealthStatus is declare in my UcBase class that I use as a base class for my UserControls:
public class UcBase : UserControl
{
public static readonly DependencyProperty HealthStatusProperty = DependencyProperty.Register("HealthStatus", typeof(HealthStatus.Statuses), typeof(UcBase));
public HealthStatus.Statuses HealthStatus
{
get { return (HealthStatus.Statuses)this.GetValue(HealthStatusProperty); }
set { this.SetValue(HealthStatusProperty, value); }
}
The two bindings you showed do different things.
<TextBlock Text="{Binding ElementName=mySelf, Path=HealthStatus}" />
Looks for a property "HealthStatus" on the control "mySelf"
While
<MultiBinding Converter="{StaticResource styleConverter}">
<MultiBinding.Bindings>
<Binding ElementName="mySelf"/>
<Binding Path="HealthStatus"/>
</MultiBinding.Bindings>
</MultiBinding>
Looks for 2 values: "mySelf" which is a Control and the "HealthStatus" property.
Since you didn't clarify where to look for the last one, it will look in the DataContext for a property called HealthStatus!
Then once it got those 2 values, it will convert them using your IMultiValueConverter, that should return a Style
Now i'm just guessing at what you really want to achieve, but if you want to bind to the "HealthStatus" property, you will need to change the MultiBinding to:
<MultiBinding Converter="{StaticResource styleConverter}">
<MultiBinding.Bindings>
<Binding ElementName="mySelf"/>
<Binding Path="HealthStatus" ElementName="mySelf"/>
</MultiBinding.Bindings>
</MultiBinding>
EDIT: To answer the comment, you couldn't do this with a simple converter, because you need to know the UserControl where the style you want to apply resides, otherwise how will you find it?
I guess in your converter you decide which style to use based on "HealthStatus" then you use the FindResource("Resource_Name") method to fetch it.