Add comma to string inside StringFormat in wpf - wpf

I have this textblock
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1}, {2}, ">
<Binding Path="object.strProp1" />
<Binding Path="object.strProp2" />
<Binding Path="object.strProp3" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Let's assume object is not null and *strProp1* = "strProp1", *strProp2* = "strProp2", and *strProp2* = "strProp2".
The output for this would be something like this:
strProp1, strProp2, strProp3,
What I would like to know is how to remove the ',' whenever object is null or one of the properties is empty. That is, if object is null then the Textblock would just be empty. Or if one of the objects is empty then it will just be empty.
Any recommendations on how to this? Thanks!
Edit: preferably in xaml only :)

I know this is an old question but I was doing a similar thing and came up with this solution. You just need to escape the ',' as you would escape special characters in a string with a '\'.
So your binding would be:
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}\, {1}\, {2}\, ">
<Binding Path="object.strProp1" />
<Binding Path="object.strProp2" />
<Binding Path="object.strProp3" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>

You have to use a Converter
MultiValueConverter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace DataBinding
{
public class MultiStringConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (values != null)
{
StringBuilder formattedString = new StringBuilder();
int count = 0;
foreach (var item in values)
{
if (string.IsNullOrEmpty((String)item) == false)
{
if (count == 0)
formattedString.Append(item);
else
formattedString.Append(", " + item);
count++;
}
}
return formattedString.ToString();
}
else
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("Cannot convert back");
}
}
}
XAML
<Window x:Class="DataBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataBinding"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:MultiStringConverter x:Key="multiStringConverter"/>
</Window.Resources>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource multiStringConverter}">
<Binding Path="object.strProp1" />
<Binding Path="object.strProp2" />
<Binding Path="object.strProp3" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Window>

<TextBlock TextWrapping="Wrap">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Address.FullAddress}" Value="">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<Run Text="{Binding Address.LineOne}"/>
<Run Text="{Binding
Address.LineTwo,
StringFormat='{}{0}, ',
TargetNullValue=''}"/>
<Run Text="{Binding
Address.City,
StringFormat='{}{0}, ',
TargetNullValue=''}"/>
<Run Text="{Binding Address.StateProvince}"/>
<Run Text="{Binding
Address.Zip,
StringFormat='{}{0}, ',
TargetNullValue=''}"/>
<Run Text="{Binding Address.Country}"/>
</TextBlock>

Related

Why the width of column in the datagrid is not set with the multivalue converter?

I want to set the width of a column with a multivalue converter, but the width is not set.
Then converter, for testing, always return 500. The xaml code is this:
<DataGridTextColumn Header="Modelo"
Binding="{Binding Modelo}"
HeaderStyle="{StaticResource DataGridColumnHeaderLeftAlignement}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource ResourceKey=DataGridCellLeftHorizontalAlignment}">
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource docListadosDocumentosDataGridComponentesColumnWidthMultiValueConverter}">
<MultiBinding.Bindings>
<Binding />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
I am trying to set the width of the cell. Perhaps should I set up the width of of another element of the visual tree?
Thanks.
EDIT: a simple example
The view model:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using System.Runtime.CompilerServices;
using System.Globalization;
namespace ModificarColumnaDataGridConMultivalueConverter
{
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
ObservableCollection<Data> col = new ObservableCollection<Data>();
col.Add(new Data() { SomeString = 44 });
col.Add(new Data() { SomeString = 84 });
col.Add(new Data() { SomeString = 104 });
cvs.Source = col;
}
private CollectionViewSource cvs = new CollectionViewSource();
public ICollectionView View { get => cvs.View; }
private double _widthValue = 30;
public double WidthValue
{
get => this._widthValue;
set { this._widthValue = value; OnPorpertyChanged(); }
}
private bool _widthDefault = false;
public bool WidthDefault
{
get => this._widthDefault;
set { this._widthDefault = value; OnPorpertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void OnPorpertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public class Data
{
public double SomeString { get; set; }
}
public class MyMultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (double)200;
double w = 30;
if (!(bool)values[1]) double.TryParse(values[0].ToString(), out w);
return w;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
The view:
<Window x:Class="ModificarColumnaDataGridConMultivalueConverter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ModificarColumnaDataGridConMultivalueConverter"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:ViewModel x:Key="vm"/>
<local:MyMultiValueConverter x:Key="MyMultiValueConverter"/>
</Window.Resources>
<StackPanel DataContext="{StaticResource vm}">
<DataGrid Name="dg" ItemsSource="{Binding View}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Col1" Binding="{Binding SomeString}" MinWidth="0" MaxWidth="300" >
<DataGridTextColumn.Width>
<MultiBinding Converter="{StaticResource MyMultiValueConverter}">
<Binding Path="WidthValue" Source="{StaticResource vm}"/>
<Binding Path="WidthDefault" Source="{StaticResource vm}"/>
</MultiBinding>
</DataGridTextColumn.Width>
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource MyMultiValueConverter}">
<Binding Path="WidthValue" Source="{StaticResource vm}"/>
<Binding Path="WidthDefault" Source="{StaticResource vm}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<CheckBox IsChecked="{Binding WidthDefault}" Content="Default Width"/>
<Slider Width="200" Height="20" Minimum="5" Maximum="300" Value="{Binding WidthValue}"/>
<!--<TextBox Text="{Binding WidthValue}"/>-->
</StackPanel>
</Window>
The problem with this solution is that with the slide, I can encrease the width of the column, but I can't decrease.
Another problem is if in the converter I return just a value, for example 500, it is not setted.
NOTE: I have realised that if I cast to double the value it works.
But the problem is that I can increase the width with the slide, but not decrease.
The converter should return a DataGridLength for the width of the column to be set:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double w = 30;
if (!(bool)values[1]) double.TryParse(values[0].ToString(), out w);
return new DataGridLength(w);
}
This means that you cannot use the same converter to set the Width of the column and the TextBlock since the latter expects a double.

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.

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.

WPF DataGrid ClipboardBinding Multibinding possible?

is there a solution to bind multiple properties to my ClipboardBinding.
I tried the following code but this didnt work:
<DataGridTemplateColumn CanUserSort="True" SortMemberPath="Characteristic.Area.Name.ActualTranslation" MinWidth="120" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5,0,5,0">
<TextBlock Text="{Binding Characteristic.Area.Name.ActualTranslation}"></TextBlock>
<TextBlock Text=" "></TextBlock>
<TextBlock Text="{Binding AreaItem.Value}"></TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{lex:Loc Area}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.ClipboardContentBinding>
<!-- TODO: ClipboardBinding Area -->
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="Characteristic.Area.Name.ActualTranslation" />
<Binding Path="AreaItem.Value" />
</MultiBinding>
</DataGridTemplateColumn.ClipboardContentBinding>
</DataGridTemplateColumn>
i'd appreciate adivce for a workaround too.
Please help
You should use converter (msdn).
class StringFormatConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return string.Format(parameter.ToString(), values);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<DataGridTemplateColumn.ClipboardContentBinding>
<MultiBinding
ConverterParameter=" {0} {1}"
Converter="{StaticResource conString}">
<Binding Path="Characteristic.Area.Name.ActualTranslation" />
<Binding Path="AreaItem.Value" />
</MultiBinding>
</DataGridTemplateColumn.ClipboardContentBinding>

How to position tooltip bottom center

I'm trying to position my tooltip so that it would be on the bottom and center of my target object. I can position it to be just on the bottom by ToolTipService.Postion="Bottom", but how to position it to be also on the center?
I agree, the options available for positioning a ToolTip are a little limited. I think you'll have to combine Placement="Bottom" with HorizontalOffset to get Bottom/Center positioning.
To center the ToolTip relative to the PlacementTarget you can use
(PlacementTarget.ActualWidth / 2.0) - (ToolTip.ActualWidth / 2.0)
Example
<Button Content="Test">
<Button.ToolTip>
<ToolTip Content="ToolTip Text"
Placement="Bottom">
<ToolTip.HorizontalOffset>
<MultiBinding Converter="{StaticResource CenterToolTipConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="PlacementTarget.ActualWidth"/>
<Binding RelativeSource="{RelativeSource Self}" Path="ActualWidth"/>
</MultiBinding>
</ToolTip.HorizontalOffset>
</ToolTip>
</Button.ToolTip>
</Button>
CenterToolTipConverter
public class CenterToolTipConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.FirstOrDefault(v => v == DependencyProperty.UnsetValue) != null)
{
return double.NaN;
}
double placementTargetWidth = (double)values[0];
double toolTipWidth = (double)values[1];
return (placementTargetWidth / 2.0) - (toolTipWidth / 2.0);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
If you need to center several ToolTips you could use a Style like
<Style x:Key="centeredToolTip" TargetType="ToolTip">
<Setter Property="HorizontalOffset">
<Setter.Value>
<MultiBinding Converter="{StaticResource CenterToolTipConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="PlacementTarget.ActualWidth"/>
<Binding RelativeSource="{RelativeSource Self}" Path="ActualWidth"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
<!-- ... -->
<Button Content="Test">
<Button.ToolTip>
<ToolTip Style="{StaticResource centeredToolTip}"
Placement="Bottom"
Content="ToolTip Text"/>
</Button.ToolTip>
</Button>

Resources