WPF pass ActualWidth and ActualHeight of child control to value converter - wpf

Using WPF, I wish to create an image for each data set in a collection and display the images stacked vertically. All the images must have the same height (and width). The images stacked together must make up the height of the containing element. The images must not be stretched in any way.
A non-working pretend example of the kind of thing I'm after is as follows.
<UniformGrid Columns="1" DataContext="{Binding DataSetCollection}">
<UniformGrid.Children>
<MultiBinding Converter="{StaticResource DataSetToImageConverter}">
<Binding />
<Binding Path="ActualWidth" RelativeSource="{RelativeSource AncestorType={x:Type UniformGridRow}}" />
<Binding Path="ActualHeight" RelativeSource="{RelativeSource AncestorType={x:Type UniformGridRow}}" />
</MultiBinding>
</UniformGrid.Children>
</UniformGrid>
This doesn't work for a number of reasons but hopefully communicates the intent. Key requirements are:
All children must be the same height
No stretching is allowed
Each image is created dynamically based on the area available to it
Images are redrawn when the size of the containing element changes
The value converter used to return the images correctly receives the width and height of the containing row
It doesn't have to use UniformGrid, it can be anything, however it should use databinding and a converter for each child.

This did the trick, binding to the size of the containing ContentPresenter.
<ItemsControl ItemsSource="{Binding DataSetCollection}" ItemTemplate="{StaticResource WriteableBitmapDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Using this data template
<DataTemplate x:Key="WriteableBitmapDataTemplate">
<Image>
<Image.Source>
<MultiBinding Converter="{StaticResource RangeToWritableBitmapConverter}">
<Binding />
<Binding Path="ActualWidth" RelativeSource="{RelativeSource AncestorType={x:Type ContentPresenter}}" />
<Binding Path="ActualHeight" RelativeSource="{RelativeSource AncestorType={x:Type ContentPresenter}}" />
</MultiBinding>
</Image.Source>
</Image>
</DataTemplate>

Related

WPF - Binding a Line with a converter that returns a Line

I'm attempting to draw some scatter charts with WPF, and struggling to draw the little tick marks usually present on chart axes. My view model defines the positions of the ticks using a List<Tick> for each axes, where each Tick item is just the cartesian coordinate of where the tick would fall on the axis. I have an IMultiValueConverter which uses the canvas size to convert the single point into a Line object that can be rendered... but how do I bind this? WPF wants me to specify X1, X2, Y1, and Y2, but I just want to give it the line.
My thought was to define a data template like the following, but it does not compile because I can't give Line a multibinding directly. Is there an intermediate element / attribute I could be using?
<DataTemplate DataType="{x:Type viewModel:Tick}" x:Key="TickTemplate">
<Line>
<WhatsMissing?>
<MultiBinding Converter="{StaticResource PointsConverter}">
<Binding Path="Points"/>
<Binding Path="DataContext.MinPoint" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"/>
<Binding Path="DataContext.MaxPoint" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"/>
<Binding Path="ActualWidth" ElementName="ChartCanvas"/>
<Binding Path="ActualHeight" ElementName="ChartCanvas"/>
</MultiBinding>
</WhatsMissing?>
</Line>
</DataTemplate>
You could bind a ContentControl's Content property to the Line returned from you converter, but IMO a converter should not deal with UIElements. Creating and manipulating UIElements in code behind should generally be avoided.
Better return a LineGeometry from your converter, and bind a Path's Data property to the result:
<DataTemplate DataType="{x:Type viewModel:Tick}" x:Key="TickTemplate">
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<MultiBinding Converter="{StaticResource PointsConverter}">
...
</MultiBinding>
</Path.Data>
</Path>
</DataTemplate>

MultiBinding ComboBoxItem.Content

Hi I am trying to achieve a binding like this:
<ComboBoxItem Style="{StaticResource ComboBoxItemStyle2}">
<ComboBoxItem.Content>
<MultiBinding StringFormat=" {}{0} {1}">
<Binding Path="Value" Source="{StaticResource Name}" />
<Binding Path="Name" Source="{StaticResource Person}" />
</MultiBinding>
</ComboBoxItem.Content>
</ComboBoxItem>
Where "Name" is a localized string and "Value" is used to get it's localized string.
for some reason this doesn't seems to work. I am getting empty string.
This might help you: String format using MultiBinding?
Taken from that post:
You are trying to bind a string to an object. But StringFormat requires its target to be a string type. Try putting a TextBlock in your content and bind your data to it.
Also put "" around Name.
Following is the corrected code:
<ComboBoxItem Style="{StaticResource ComboBoxItemStyle2}">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="Value" Source="{StaticResource Name}" />
<Binding Path="Name" Source="{StaticResource Person}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</ComboBoxItem>
I need to fix two things:
Keep the content as TextBlock and do binding of text there.
Removed extra spacing from StringFormat i.e. " {}{0} {1}" ==> "{}{0} {1}".
You Can see this link i wish be helpful
https://social.msdn.microsoft.com/Forums/vstudio/en-US/32b81578-b201-4927-bdc2-ebb9a42ae303/comboboxdisplaymemberpath-and-multibinding

Sort Column of DataGrid using a MultiValueConverter

I have a datagrid with a textcolum using a MultiValueConverter. The converter got 2 values. The first depend of the current Item and the second of a TextBlock. The value displayed is what I want.
<TextBox x:Name="phases"></TextBox>
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserResizeRows="False"
ItemsSource="{Binding MySource}" RowDetailsVisibilityMode="Collapsed" RowHeaderWidth="0"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Width="Auto" Header="Pos">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource MyMultiValueConverter}">
<Binding ElementName="phases" Path="Text" />
<Binding />
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid >
The Value display in the column is what I want.
The problem is that I can't sort by this colum.
I try to add something like that:
<DataGridTextColumn.SortMemberPath>
<MultiBinding Converter="{StaticResource MyMultiValueConverter}">
<Binding ElementName="phases" Path="Text" />
<Binding />
</MultiBinding>
</DataGridTextColumn.SortMemberPath>
But I get an "Cannot find governing FrameworkElement or FrameworkContentElement for target element." error. I change to:
<DataGridTextColumn.SortMemberPath>
<MultiBinding Converter="{StaticResource MyMultiValueConverter}">
<Binding Path="Text" Source="{x:Reference phases}" />
<Binding Path="" />
</MultiBinding>
</DataGridTextColumn.SortMemberPath>
Then the first line is ok, but for the second, I can't get the currentItem.
I try to used SortEvent, but I can only add SortDescription without any logic ( I have it in my converter).
Is any way to sort a column when using a multiValueConverter?
I think this is the same question as here: DataGridColumn SortMemberPath on MultiBinding
SortMemberPath is expecting the name of a property (e.g. "TotalDollars") not an individual computed row value. Think of it like the header, you set it once for the whole column. Your converter would be returning a number like 15 where SortMemberPath wants a binding path string.
Two options that come to mind:
Provide a computed property on your backing object (e.g. "AveragePrice") and bind to that. No converter or sort member path necessary.
public double AveragePrice
{
get { return TotalDollars / NumberToDivideBy; }
}
Specify an OnSorting event handler like in this question.
Hope it helps. :)

Canvas height & width bind to xml

In the past I created a winform user control that inherit imagebox control, which the user can select part of the image by drawing a rectangle with the mouse.
Now I want to do the same with WPF. I wrote this XAML code:
<StackPanel Width="321" Height="241">
<Canvas x:Name="SampleImageCanvas"
MouseMove="SampleImageCanvas_MouseMove"
MouseDown="SampleImageCanvas_MouseDown"
MaxHeight="240" MaxWidth="320" >
<Canvas.Width>
<MultiBinding Converter="{StaticResource SizeConverter}" Mode="OneWay">
<Binding XPath="./Size/Width" />
<Binding XPath="./Size/Ratio" />
</MultiBinding>
</Canvas.Width>
<Canvas.Height>
<MultiBinding Converter="{StaticResource SizeConverter}" Mode="OneWay">
<Binding XPath="./Size/Height" />
<Binding XPath="./Size/Ratio" />
</MultiBinding>
</Canvas.Height>
<Canvas.Background>
<ImageBrush x:Name="SampleImage" ImageSource="{Binding XPath=./SampleImage}" Stretch="Uniform"/>
</Canvas.Background>
<Rectangle x:Name="ROI"
Canvas.Top="{Binding XPath=./ROI/Top, Mode=TwoWay}"
Width="{Binding XPath=./ROI/Width, Mode=TwoWay}"
Height="{Binding XPath=./ROI/Height, Mode=TwoWay}"
Canvas.Left="{Binding XPath=./ROI/Left, Mode=TwoWay}"
Stroke="#FFD13750"
/>
</Canvas>
</StackPanel>
The data is stored in an xml document. the image is bigger than the canvas, so it has to be stretch uniformly, to fit the max size of the canvas, and I want the canvas to resize to the size of the image scaled display, so the image fills 100% of the canvas with no margins.
when I'll have this, the mouse move/enter/exit event will be set only to the image borders.
The problem is the canvas height binded value, which cause the image not to be shown although it has beec defined same as the canvas width which works, and the convertor returns the corect value.
When I set the height to exact value instead of binded value, like Height="240", it works.
Thanks!

WPF Object send itself as MultiBinding path

Basically what I need to know is how to send the source of an HierarchicalDataTemplate into a binding, this is what I have:
<HierarchicalDataTemplate DataType="{x:Type myModel:Person}">
<StackPanel Orientation="Horizontal">
<Image Source="Images\User.gif" />
<TextBlock Margin="5,0,0,0" Text="{Binding Name}" />
</StackPanel>
<HierarchicalDataTemplate.ItemsSource>
<MultiBinding Converter="{StaticResource PersonConverter}">
<Binding Path="Name" />
<!-- Here I need something like Binding Path="Self" so I can send the source of the binding (the "Person" object) -->
</MultiBinding>
</HierarchicalDataTemplate.ItemsSource>
</HierarchicalDataTemplate>
So my source is an object of type myModel:Person, I want to be able to send the object itself in the MultiBinding so the PersonConverter can use it.
Thanks for any help.
Wow, I did a crazy wild guess and it worked =S lol, here's the solution
<MultiBinding Converter="{StaticResource PersonConverter}">
<Binding Path="Name" />
<Binding Path="." /> <!-- this sends the source of the binding -->
</MultiBinding>
Thanks!

Resources