WPF MultiBinding set fallback string for a null binding - wpf

If I do not set the Date, the result becomes "price=2000, date=". Can I make it "price=2000, date=unknown" instead?
<TextBlock x:Name="Test">
<TextBlock.Text>
<MultiBinding StringFormat="{}price={0}, date={1:d}">
<Binding Path="Price" />
<Binding Path="Date" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
public partial class MainWindow : Window
{
public int Price { get; set; }
public DateTime? Date { get; set; }
public MainWindow()
{
InitializeComponent();
Price = 2000;
//Date = DateTime.Now;
Test.DataContext = this;
}
}

In this case, you can use the TargetNullValue Property of your binding:
<TextBlock x:Name="Test">
<TextBlock.Resources>
<local:DateConverter x:Key="DateConverter" />
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding StringFormat="{}price={0}, date={1:d}">
<Binding Path="Price" />
<Binding Path="Date" TargetNullValue="unknown" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>

Related

WPF MultiBinding Ellipse Fill

I am not able to get MultiBinding on Ellipse.Fill working correctly. I do have (single) Binding working correctly, plus MultiBinding on Ellipse.Tooltip:
<Ellipse Margin="210,56,0,0" Fill="{Binding InspectorPC, Converter={StaticResource statusButtonConverter}, Mode=OneWay}">
<Ellipse.ToolTip>
<MultiBinding Converter="{StaticResource statusStringConverter}" Mode="OneWay">
<Binding Path="InspectorPC"/>
<Binding Path="InspectorPCPing"/>
<Binding Path="InspectorPCReadHD"/>
</MultiBinding>
</Ellipse.ToolTip>
</Ellipse>
but I would like something like:
<Ellipse Margin="210,56,0,0">
<Ellipse.Fill>
<MultiBinding Converter="{StaticResource statusButtonConverter}" Mode="OneWay">
<Binding Path="InspectorPCPing"/>
<Binding Path="InspectorPCReadHD"/>
</MultiBinding>
</Ellipse.Fill>
<Ellipse.ToolTip>
<MultiBinding Converter="{StaticResource statusStringConverter}" Mode="OneWay">
<Binding Path="InspectorPC"/>
<Binding Path="InspectorPCPing"/>
<Binding Path="InspectorPCReadHD"/>
</MultiBinding>
</Ellipse.ToolTip>
</Ellipse>
(Obviously statusButtonConverter would need to be changed from IValueConverter to IMultiValueConverter, but that is not the issue.)
If this isn't working, it suggests a problem in your statusButtonConverter implementation.
A simple example shows no problem applying a MultiBinding to Ellipse.Fill:
<Window x:Class="WpfTest.FillMultiBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfTest2"
Width="320"
Height="160">
<Window.Resources>
<l:BrushPartsConverter x:Key="brushPartsConverter" />
</Window.Resources>
<Window.DataContext>
<l:FillViewModel />
</Window.DataContext>
<Ellipse>
<Ellipse.Fill>
<!-- Dodger + Blue = DodgerBlue -->
<MultiBinding Converter="{StaticResource brushPartsConverter}" Mode="OneWay">
<Binding Path="Part1" />
<Binding Path="Part2" />
</MultiBinding>
</Ellipse.Fill>
</Ellipse>
</Window>
public class FillViewModel
{
public string Part1 => "Dodger";
public string Part2 => "Blue";
}
public class BrushPartsConverter : IMultiValueConverter
{
private static readonly BrushConverter InnerConverter = new BrushConverter();
public object Convert(object[] values, Type type, object p, CultureInfo c)
{
if (values?.Length == 2)
return InnerConverter.ConvertFrom("" + values[0] + values[1]);
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value, Type[] types, object p, CultureInfo c)
{
return new[] { DependencyProperty.UnsetValue };
}
}
Post the code for your converter and binding context (view model), and we'll see what we can do.

MultiValueConverter reading from ObservablleCollection

I am working on a wpf mvvm project. In a user control I have a datagridControl from Devexpress that is bound to data from a Observable collection.
<xcdg:DataGridControl x:Name="DataGridName" HorizontalAlignment="left" VerticalAlignment="Stretch"
AutoCreateColumns="False"
ItemsSource="{Binding ViewModel.Items}"
ItemScrollingBehavior="Immediate" SynchronizeCurrent="True" TabIndex="69" >
<xcdg:DataGridControl.Columns >
<xcdg:Column FieldName="Name" AllowSort="False" Title="Name" ShowInColumnChooser="False" />
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
The class in the Observable collection Contains a Name (string) and IsVerified (Boolean).
private ObservableCollection<myData> _items = new ObservableCollection<myData>();
public ObservableCollection<myData> Items
{
get { return _items; }
set { _items = value; }
}
public class myData
{
public string Name { get; set; }
public bool IsVerfied { get; set; }
}
I also have a textblock that I use to display an error message above the dataGrid when the Value of IsVerfied is false.
<TextBlock Name="textBlockErrrMessage" Foreground="IndianRed">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MultiValueConverter}">
<Binding Path="DataContext.IsVerified" RelativeSource="{RelativeSource AncestorType=xcdg:DataRow}" ElementName="DataGridName" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
To do this I plan on having a multivalueconverter (I am also doing the same thing but for a different control so that is why I choose a MultiValueConverter) that I would like to send the IsVerfied value from the Collection and return the message. My issue is how do I set the Binding in the MultiBinding to read the IsVerfied value from the Observablecollection. This particular line is what I believe is the issue in locating the Collection value
<Binding
Path="DataContext.IsVerified"
RelativeSource="{RelativeSource AncestorType=xcdg:DataRow}"
ElementName="DataGridName" />
In your Binding, you want to use either RelativeSource or ElementName, but not both. See this post for a good clarification on the differences between the two.

Applying a (non-multi) ValueConverter the output of a MultiBinding + StringFormat

Is there a way to apply a (single, not multi) ValueConverter to the output of a MultiBinding which uses StringFormat (i.e. after the string has been formatted).
It would be the equivalent of that code, in which I used an intermediary collapsed TextBlock to do the trick :
<StackPanel>
<TextBox x:Name="textBox1">TB1</TextBox>
<TextBox x:Name="textBox2">TB2</TextBox>
<TextBlock x:Name="textBlock" Visibility="Collapsed">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}{1}">
<Binding ElementName="textBox1" Path="Text"/>
<Binding ElementName="textBox2" Path="Text"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding ElementName=textBlock,
Path=Text, Converter={StaticResource SingleValueConverter}}" />
</StackPanel>
Here is a hack that does what you want:
public static class Proxy
{
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(Proxy),
new PropertyMetadata(string.Empty));
public static void SetText(this TextBlock element, string value)
{
element.SetValue(TextProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(TextBlock))]
public static string GetText(this TextBlock element)
{
return (string) element.GetValue(TextProperty);
}
}
<StackPanel>
<TextBox x:Name="textBox1">TB1</TextBox>
<TextBox x:Name="textBox2">TB2</TextBox>
<TextBlock Text="{Binding Path=(local:Proxy.Text),
RelativeSource={RelativeSource Self},
Converter={StaticResource SingleValueConverter}}">
<local:Proxy.Text>
<MultiBinding StringFormat="{}{0}{1}">
<Binding ElementName="textBox1" Path="Text" />
<Binding ElementName="textBox2" Path="Text" />
</MultiBinding>
</local:Proxy.Text>
</TextBlock>
</StackPanel>
If you look at the MultiBinding.Converter Property page on MSDN, you will see that you can provide a Converter for a MultiBinding. However, it is not a normal IValueConverter, instead it requires an IMultiValueConverter. It can be used like this:
<TextBlock x:Name="textBlock" Visibility="Collapsed">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}{1}" Converter="{StaticResource Converter}"
ConverterParameter="SomeValue">
<Binding ElementName="textBox1" Path="Text"/>
<Binding ElementName="textBox2" Path="Text"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
An example of an IMultiValueConverter implementation can be found in the linked pages.

How to bind class with Dictionary<string,object> property to datagrid

I have a scenario where i have to dynamically fetch the data and store it in a dictionary for display.
This is how my Model looks like:
public class ViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public Dictionary<string,object> CustomFields { get; set; }
}
I am binding my view model to DataGrid and i have two columns defined for Id and Name.
How do i generate columns for the data in the dictionary?
I cannot use Dynamic object as i am using .net 3.5.
This is how my XAML looks like:
<Window x:Class="MyClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tk="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:viewmodel="clr-namespace:MyViewModel"
xmlns:model="clr-namespace:MyModel"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="List Of Projects" Height="600" Width="600">
<Window.DataContext>
<viewmodel:SomeViewModel></viewmodel:SomeViewModel>
</Window.DataContext>
<tk:DataGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="dgData" ItemsSource="{Binding ListOfObjects}" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" AutoGenerateColumns="False" SelectionMode="Single" CanUserAddRows="False" CanUserDeleteRows="False" >
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Header="Id" Binding="{Binding Id}" IsReadOnly="True"></tk:DataGridTextColumn>
<tk:DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True"></tk:DataGridTextColumn>
</tk:DataGrid.Columns>
</tk:DataGrid>
</Window>
Use a Converter :
CS :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
public Dictionary<int, string> MyDictionary
{
get
{
var dic = new Dictionary<int, string>();
dic.Add(1,"11111");
dic.Add(2,"22222");
dic.Add(3,"33333");
dic.Add(4,"44444");
return dic;
}
}
}
public class DictionaryConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DataGridRow row = (DataGridRow)values[0];
IDictionary<int,string> dic = (IDictionary<int,string>)values[1];
bool isId = bool.Parse(parameter.ToString());
var index = row.GetIndex();
KeyValuePair<int,string> kv = dic.ElementAt(index);
return isId ? kv.Key.ToString() : kv.Value;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML :
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding MyDictionary}" CanUserAddRows="False">
<DataGrid.Resources>
<loc:DictionaryConverter x:Key="dicConverter" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="ID">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource dicConverter}" ConverterParameter="True">
<Binding Path="." RelativeSource="{RelativeSource AncestorType=DataGridRow}" />
<Binding Path="ItemsSource" RelativeSource="{RelativeSource AncestorType=DataGrid}" />
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Name">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource dicConverter}" ConverterParameter="False">
<Binding Path="." RelativeSource="{RelativeSource AncestorType=DataGridRow}" />
<Binding Path="ItemsSource" RelativeSource="{RelativeSource AncestorType=DataGrid}" />
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>

How to avoid default text in textbox?

I have a textbox, which uses multi-binding usingStringFormat...as shown below.
But it displays the default value as
{DependencyProperty.UnsetValue},{DependencyProperty.UnsetValue}
How to avoid this ?
<StackPanel Orientation="Horizontal">
<TextBlock Width="70" Text="Name:" Margin="5,2,2,2"></TextBlock>
<TextBox Width="160" DataContext="{Binding }" IsReadOnly="True" Margin="2">
<TextBox.Text>
<MultiBinding StringFormat="{}{0},{1}">
<Binding Path="LastName"/>
<Binding Path="FirstName"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
Please help me.
Something is wrong with the object you are binding to. I just created an application from scratch with a Person class that inherits from DependencyObject. I left the first and last name properties unset and I did not see DependencyProperty.UnsetValue, but rather a blank TextBox with just a comma in it.
(In general, you shouldn't use dependency properties on your business objects anyway. Stick to INotifyPropertyChanged and save yourself a ton of headaches.)
Post the code to your bound object and maybe I can spot the issue.
public class Person : DependencyObject
{
public static readonly DependencyProperty FirstNameProperty = DependencyProperty.Register("FirstName", typeof(string), typeof(Person), new FrameworkPropertyMetadata());
public string FirstName {
get { return (string)GetValue(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
public static readonly DependencyProperty LastNameProperty = DependencyProperty.Register("LastName", typeof(string), typeof(Person), new FrameworkPropertyMetadata());
public string LastName {
get { return (string)GetValue(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
}
-
<TextBox IsReadOnly="True">
<TextBox.Text>
<MultiBinding StringFormat="{}{1}, {0}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
</TextBox.Text>
</TextBox>
-
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
private void Window_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var p = new Person();
//p.FirstName = "Josh";
//p.LastName = "Einstein";
DataContext = p;
}
}
just give the fallbackvalue="" and see
<TextBox.Text>
<MultiBinding StringFormat="{}{0},{1}">
<Binding Path="LastName" FallbackValue=""/>
<Binding Path="FirstName" FallbackValue=""/>
</MultiBinding>
</TextBox.Text>
If a binding is not successful, i.e. the path to the binding source is not found or the value converter , if any, fails, a DependencyProperty.UnsetValue is returned, then the target property is set to the FallbackValue, if you defined one of course.

Resources