I have a datagrid and I want to set the width of a column according to some values, so I am trying to use a multibinding in this way:
<DataGridTextColumn.Width>
<MultiBinding Converter="{StaticResource MyMultiValueConverter}">
<Binding Source="{x:Reference ProxyElement}" Path="DataContext.MyProperty" />
<Binding Source="0"/>
</MultiBinding>
</DataGridTextColumn.Width>
The converter is fired, but the width is not changed according to de value it returns.
However, I have a similiar converter to set the visibility and it works as expected:
<DataGridTextColumn.Visibility>
<MultiBinding Converter="{StaticResource MyConverterVisibilityMultiValueConverter}">
<Binding Source="{x:Reference ProxyElement}" Path="DataContext.NombreProveedor" />
<Binding Source="0"/>
</MultiBinding>
</DataGridTextColumn.Visibility>
Why does it work with the visibivilty but not with the width?
Thanks.
EDIT:
I have tried setting the width of the textblock inside the column in this way:
Value converter:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//I have tried both ways, DataGridLength and return 20
//return new DataGridLength(20, DataGridLengthUnitType.SizeToHeader);
return 20;
}
Xaml: Option 1, setting the width directly, it works
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Width" Value="20"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
Xaml: option 2: trying with value converter, it doesn't work
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Width">
<Setter.Value>
<MultiBinding Converter="{StaticResource MyMultiValueConverter}" >
<Binding Source="{x:Reference ProxyElement}" Path="DataContext.Property1" />
<Binding Source="{x:Reference ProxyElement}" Path="DataContext.Property2" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
The converter is rised and it returns 20, but the column doesn't take this value.
Ensure that the converter returns a DataGridLength value:
return new DataGridLength(100, DataGridLengthUnitType.Pixel);
Related
Unable to remove whitespace from StringFormat.
If no 'Type' data the following whitespace doesn't fallback.
ReleaseDate, space, space, dot, space, space, Type, space (some reason it doesn't remove this space), Color
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:yyyy} · {1} {2}" >
<Binding Path="ReleaseDate" FallbackValue="{x:Null}"/>
<Binding Path="Type" FallbackValue="{x:Null}"/>
<Binding Path="Color" FallbackValue="{x:Null}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
You may probably replace the MultiBinding and its StringFormat by a DataTrigger:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text">
<Setter.Value>
<MultiBinding StringFormat="{}{0:yyyy} · {1} {2}" >
<Binding Path="ReleaseDate" FallbackValue="{x:Null}"/>
<Binding Path="Type"/>
<Binding Path="Color" FallbackValue="{x:Null}"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="{x:Null}">
<Setter Property="Text">
<Setter.Value>
<MultiBinding StringFormat="{}{0:yyyy} · {1}" >
<Binding Path="ReleaseDate" FallbackValue="{x:Null}"/>
<Binding Path="Color" FallbackValue="{x:Null}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
While the approach with DataTrigger works, it is very verbose. I would make it a two-step process: bind Type separately with a StringFormat which has a space, then bind everything together
<TextBlock Name="Title"
local:Attached.SomeString="{Binding Type,
StringFormat='\{0} ',
TargetNullValue=''}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:yyyy} · {1}{2}">
<Binding Path="Login" />
<Binding Path="(local:Attached.SomeString)"
RelativeSource="{RelativeSource Self}" />
<Binding Path="Name" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
If Type is not null then SomeString binding uses string format, and returns Type + space at the end. If Type is null, then binding return empty string.
SomeString is a reusable attached dependency property without special meaning. It declared as
public class Attached
{
public static readonly DependencyProperty SomeStringProperty =
DependencyProperty.RegisterAttached("SomeString", typeof (string), typeof (Attached));
public static void SetSomeString(UIElement element, string value)
{
element.SetValue(SomeStringProperty, value);
}
public static string GetSomeString(UIElement element)
{
return (string)element.GetValue(SomeStringProperty);
}
}
TextBlock has Tag property which can be used to store (or bind) some data, but Tag is of type object, and doesn't use StringFormat from binding. So I couldn't use Tag for my purpose here, and had to create another property, which has string type. (note also special syntax to bind attached property)
Due to some shortcomings with CodedUI, it's difficult to select items in the DataGrid. One work around that I've found is to override the ItemContainerStyle like so:
<DataGrid.ItemContainerStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="AutomationProperties.AutomationId">
<Setter.Value>
<MultiBinding StringFormat="ArisingID_{0}">
<Binding Path="(DataGridRow.Tag)" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="AutomationProperties.Name">
<Setter.Value>
<MultiBinding StringFormat="ArisingID_{0}">
<Binding Path="(DataGridRow.Tag)" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ItemContainerStyle>
and then populate each Row.Tag with a unique ID in the code behind:
private void MyDataGrid_OnLoadingRow(object sender, DataGridRowEventArgs e)
{
var item = e.Row.Item as IMyViewModel;
e.Row.Tag = item.UniqueSeqId;
}
However, one issue is that this overrides some of the "default style" of the data grid row - each cell on the data grid seems to be separately selectable, rather than it behaving as just one row.
What's the best way to incorporate the default styles along with these modifications?
There is a BasedOn property that will set your style, and then add the extra setters you've added:
<DataGrid.ItemContainerStyle>
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource DataGridRowStyle}">
<Setter Property="AutomationProperties.AutomationId">
<Setter.Value>
<MultiBinding StringFormat="ArisingID_{0}">
<Binding Path="(DataGridRow.Tag)" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="AutomationProperties.Name">
<Setter.Value>
<MultiBinding StringFormat="ArisingID_{0}">
<Binding Path="(DataGridRow.Tag)" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
I have create a DataTemplate for my Component object. I added DataTrigger to determine if the component should be visible or not. Essentially we have filters and the code checks those filters to determine if the component should be Visible or Collapse. The issue I have is that I want the trigger to set the visibility to "Collapse" or "Visible" of the parent container, i.e a ListBoxItem. The code works but sets it at the Border instead.
The template starts like this:
<DataTemplate DataType="{x:Type local:Component}">
<Border .....
I am providing the code for my Trigger and I'll explain what I tried below without success.
<DataTemplate.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource TrueWhenComponentIsVisible}">
<Binding Path="Type" />
<Binding Path="Dependency"/>
<Binding Path="SelectedType" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}"/>
<Binding Path="SelectedDepencency" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}"/>
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter Property="Visibility" Value="Visible"></Setter>
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Value="False">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource TrueWhenComponentIsVisible}">
<Binding Path="Type" />
<Binding Path="Dependency"/>
<Binding Path="SelectedType" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}"/>
<Binding Path="SelectedDepencency" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}"/>
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger.Setters>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
For the Setter Property
<Setter Property="Visibility" Value="Collapsed"></Setter>
I attempted to use the binding to get the listboxitem like this:
<Setter Property="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem}, Path=Visibility}" Value="Collapsed"></Setter>
I get this error when I try to run it, so I assume I can't use binding there at all and need a different approach?
A 'Binding' cannot be set on the 'Property' property of type 'Setter'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject
Worked for me.
Don't know why it did not work for you?
<ListBox x:Name="lb" ItemsSource="{Binding}" DisplayMemberPath="Text">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Visibility" Value="{Binding Path=Vis}" />
</Style>
</ListBox.Resources>
</ListBox>
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
List<TextVis> TextViss = new List<TextVis>();
TextVis tv1 = new TextVis();
tv1.Text = "tv1";
tv1.Vis = System.Windows.Visibility.Hidden;
TextViss.Add(tv1);
TextVis tv2 = new TextVis();
tv2.Text = "tv2";
tv2.Vis = System.Windows.Visibility.Visible;
TextViss.Add(tv2);
lb.ItemsSource = TextViss;
}
public class TextVis
{
public string Text { get; set; }
public Visibility Vis { get; set; }
}
}
I want the trigger to set the visibility to "Collapse" or "Visible" of
the parent container, i.e a ListBoxItem
Change the ItemContainerStyle, like this:
<ListBox ...>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource TrueWhenComponentIsVisible}">
<Binding Path="Type" />
<Binding Path="Dependency"/>
<Binding Path="SelectedType" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}"/>
<Binding Path="SelectedDepencency" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
BTW, you should really create a proper ViewModel and move all this logic to the ViewModel level instead of so much MultiBinding and Converter-based stuff.
I am trying to create a style for comboxitem. I want to pass the current comboboxitem to converter. Style is like
<Style x:Key="MyVisibilityStyle" TargetType="{x:Type ComboBoxItem}">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource VisibiltyMultiValueConverter}">
<Binding Path="."/>
<Binding Path="SelectedItem"
ElementName="ABCComboBox"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
Problem is the "." is passing the object of MainWindow not the comboboxitem.
Through <Binding Path="."> youre passing the object which the ComboBoxItem holds, but with <Binding RelativeSource="{RelativeSource Self}"/> you can pass the control itself.
What you also could do is passing the whole ComboBox and its selected index/item:
and in your converter you could get your ComboBoxItem like so:
ComboBoxItem cbi = (ComboBoxItem)(cb.ItemContainerGenerator.ContainerFromIndex(selectedindex));
or
ComboBoxItem cbi = (ComboBoxItem)(cb.ItemContainerGenerator.ContainerFromItem(selecteditem));
You can get the selected item of the combobox by using FindAncestor:
<Binding Path="SelectedItem" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ComboBox}"/>
I have a DataGrid, in which I want to change the background of a row according to the values of the ItemSource, so I need to pass the current item, but I don't know how.
I am doing that:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource myDataGridBackgroundMultiValueConverter}">
<MultiBinding.Bindings>
<Binding ElementName="ucPrincipal" Path="DataContext.MyProperty01FromDataContext"/>
<Binding ElementName="ucPrincipal" Path="DataContext.MyProperty02FromDataContext"/>
<Binding ElementName="ucPrincipal" Path="DataContext.MyProperty03FromDataContext"/>
<Binding ElementName="dgdMyGrid" Path="CurrentItem"/>
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
The DataGrid has as ItemsSource a collection of MyDataClass (that has many properties) in the ViewModel. I need to pass to the converter the MyDataClass with the information of the row.
CurrentItem does not work, because I always receive null.
All the other parameters are ok.
DataGrid has no CurrentItem property, only CollectionViews have, DataGrid has a SelectedItem. You should see a binding error because of this as well.
If by current item you mean the data item to which the styled row belongs that would be the DataContext of the current row, which can be targeted via <Binding />.
Well, finnally, I get the way to pass the dataContext of the row to the converter. I do the following:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Visibility" Value="{Binding ChangeTracker.State, Converter={StaticResource visibilidadFilaBorradaConverter}}"/>
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource ucTareasMatenimientoDataGridBackgroundMultiValueConverter}">
<MultiBinding.Bindings>
<Binding ElementName="ucPrincipal" Path="DataContext.Property01"/>
<Binding Path="DataContext" RelativeSource="{RelativeSource Mode=Self}" />
<Binding ElementName="ucPrincipal" Path="DataContext.Property02"/>
<Binding ElementName="ucPrincipal" Path="DataContext.Property03"/>
<Binding ElementName="ucPrincipal" Path="DataContext.Property04"/>
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
In the second parameter for the multivalue converter, I use the RelativeSource, to be able to pass the datContext of the row. In this way, I can compare its information with other properties of the principal dataContext of the control.