Applying transforms from DataTemplates in WPF - c#

I've created a DataTemplate for my objects in a ResourceDictionary file. The template is basically an image that is loaded from the disk. Now, what happens is that I want to align the image to a specific point on my Canvas but not by its upper left point but its center point, that's why I want to apply a translate transform for X = -Width / 2 and
Y = -Height / 2 but I don't know how to apply them via the DataTemplate.
Any help will be appreciated, thanks!

Try using databinding on Canvas' the AttachedProperties and an IValueConverter to transform the offsets to whatever you want.
For instance:
class ImageToCanvasConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return -(int)value / 2;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Two-way binding not supported
throw new InvalidOperationException();
}
}
<Grid.Resources>
<myAssembly:ImageToCanvasConverter x:Key="imageToCanvasConverter" />
<DataTemplate ...>
<Image Canvas.Left="{Binding Path=Width, Converter={StaticResource imageToCanvasConverter}, Mode=OneTime}"
Canvas.Top="{Binding Path=Height, Converter={StaticResource imageToCanvasConverter}, Mode=OneTime}"
... />
</DataTemplate>
</Grid.Resources>

you can take the advantage of using loaded event on the data template child
Example:
if you are using grid as data template content
<DataTemplate>
<Grid Loaded="Grid_Loaded">
<Image></Image>
</Grid>
</DataTemplate>
you can write the transformation code in the .cs file using sender object.

Related

How to sort ItemsSource dynamically in C#; WPF

I use in XAML an ItemsControl where and in its ItemsSource I make a Binding for an Enum, thus creating a list of RadRadioButton dynamically, and if someday another item is added to this enumerator my code will already create this new button and show it.
<ItemsControl ItemsSource="{Binding MyEnum}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<telerik:RadRadioButton GroupName="GroupMyEnum">
<TextBlock Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
</telerik:RadRadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Today I use a converter that takes the description of the Enum and shows instead of the value of the Enum.
But besides that I would like to change the order in which my list of buttons is generated, is this possible?
Example: If my list needs to be generated elsewhere in the interface, it must be generated in a different order than the enumerator was created.
Whether an Enum has the options A, B, C, D. At one point I would like to show as the first option D instead of A.
Was I clear enough?
Define a converter that takes MyEnum, change its order based on the value of converter's parameter and return the new list with the new order (ConverterParameter is optional, do not set it if you don't want to change the order of the list).
public class MyEnumCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is IEnumerable<MyEnum> input)
{
switch (parameter)
{
case "Case1":
// todo: change the order of input
return input;
case "Case2":
// todo: change the order of input
return input;
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Define the converter in your app.xaml (or at one of ItemsControl parents) and use it like this..
<ItemsControl ItemsSource="{Binding MyEnum, Converter={StaticResource MyEnumCollectionConverter}, ConverterParameter=Case1}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<telerik:RadRadioButton GroupName="GroupMyEnum">
<TextBlock Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
</telerik:RadRadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that you can do the EnumDescriptionConverter work inside MyEnumCollectionConverter, so your TextBlock can be just
<TextBlock Text="{Binding .}"/>
The first thing that pops to mind is sorting the ItemSource itself. I believe you use Linq sort by calling List.Sort() on the source itself and the list of order on the visual layer should be altered aswell.
More information about this can be found here.

Wpf-how can I use a converter in order to display elements from a listbox while I am scrolling

I have a big lists of objects which are binded to a ListBox. In that list box i am displaying the name of a file and a byte array of an image which is coming form WCF. I would display the files in the listbox depending on the scroll postion, so the file which are not visible from that scroll position to be set as Hidden.
I found on the internet a way which might help me but i cannot see a way that this will work.
I found convertors which should help me, and set up that class as a window resource. When I am opening the app all the items are converted to be visible.
This is the class:
class NullVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Hidden : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Visible : Visibility.Hidden;
}
}
In Xaml i setup this class as a window Resource and i binded the functionality to the image and name in the Listbox items
<Window.Resources>
<view:NullVisibilityConverter x:Key="NullToVis"/>
</Window.Resources>
<Image Source="{Binding Path=Image}" Visibility="{Binding Path=Image, Converter={StaticResource NullToVis}}" Height="80" Width="80" Grid.Column="0"/>
<StackPanel Grid.Column="1" Visibility="{Binding Path=Image, Converter={StaticResource NullToVis}}" Orientation="Vertical" Width="80" Height="30">
<TextBlock Text="{Binding Path=Name}" Margin="5,1,0,1"/>
</StackPanel>
Does anyone have any idea how this might work?

Limit GridSplitter (MVVM)

Simple case, when I want to limit GridSplitter maximum, then I can create invisible control (which only participates in layouting), adjust its Margin and use to limit grid column/row definitions:
<Grid x:Name="limiter" Margin="10,10,20,10" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding StoreWidth, Converter={local:DoubleToGridLengthConverter}, Mode=TwoWay}"
MaxWidth="{Binding ActualWidth, ElementName=gridLimit}" /> <!-- limit maximum -->
...
</Grid.ColumnDefinitions>
<GridSplitter ... />
...
</Grid>
Now complicated case: nested view with GridSplitter, which has to be limited by some layout logic of parent view.
How do I do this? I am seeking for a comfortable and reusable solution.
Currently I am doing it complicated way:
add property to parent VM to bind limiter values (e.g. ActualWidth of it);
add to nested VM property Parent;
pass parent VM instance to nested one (using property initializer);
now GridSplitter can be limited by using Parent.SplitterMaxWidth binding.
Is there any nice and MVVM-friendly approach to the problem?
For cases when GridSplitter has to be limited by parent (TBH all my layouts can be converted to ensure this) it is possible to bind to that parent and use generic converter to change value. If parent is hard to reach, then make a simple custom control:
public class GridLimiter: Grid { }
which can be used to mark that container to be easily reached by RelativeSource.FindAncestor:
<local:GridLimiter ... >
... <!-- deeply nested -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GridLimiter}}}" />
...
</Grid.ColumnDefinitions>
<GridSplitter ... />
</Grid>
...
Moreover, making a simple markup extension converter:
public class ValueAddConverter : MarkupExtension, IValueConverter
{
public ValueAddConverter() { }
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
(double)value + double.Parse((string)parameter, NumberFormatInfo.InvariantInfo);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
allows to define offset without needs to create invisible limiter:
MaxWidth="{Binding ActualWidth, Converter={local:ValueAddConverter}, ConverterParameter=-30, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GridLimiter}}}"

WP8 - Binding a value with a float to String conversion

I have a couple of gauges in my app, and I can't figure out how to add a converter to the text binding. I read a couple of guides on msdn but I didn't manage to figure that out (I've been coding for WP8 for just a couple of weeks).
This is is a piece of the gauge:
<gauges:MarkerGaugeIndicator Value="0"
gauges:LinearGaugeRange.IndicatorOffset="35"
x:Name="GaugeBarValore"
IsAnimated="True">
<gauges:MarkerGaugeIndicator.MarkerTemplate>
<DataTemplate>
<Grid Width="73" Height="35" UseLayoutRounding="False" d:LayoutRounding="Auto" Margin="10,-2,0,0">
<TextBlock x:Name="GaugeBarPercent" Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
FontWeight="Thin" Margin="6,4,32,4" Width="35"/>
<Grid.RenderTransform>
<CompositeTransform Rotation="90" TranslateX="49" TranslateY="12" />
</Grid.RenderTransform>
</Grid>
</DataTemplate>
</gauges:MarkerGaugeIndicator.MarkerTemplate>
</gauges:MarkerGaugeIndicator>
The binding itself works, but I can see a lot of decimal numbers while the value moves from a round value to another.
I want to add a converter like this method:
private String double2String(double valore)
{
return Convert.ToString(Math.Round(valore)) + "%";
}
I just don't know where to put this method and how to add this as a converter inside the binding.
Thank you for your help! :)
Sergio
Create a class to hold your Converter method that implements IValueConverter interface, Example class is bellow. You have to implement method Convert and ConvertBack.
public class DoubleToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Math.Round((double)value).ToString() + "%";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return double.Parse(value as string);
}
}
then add the namespace to your XAML page.
xmlns:convert="clr-namespace:Your_project_name"
Next add your converter as a Resource type i to your XAML page..
<phone:PhoneApplicationPage.Resources>
<convert:DoubleToString x:Key="DoubleConvert" />
</phone:PhoneApplicationPage.Resources>
The x:Key value is the name we are going to call in our binding statement.
Then perform the data binding. I have a simple slider and a textblock with sliders value bound to the textblocks Text property
<StackPanel>
<Slider Name="slider" Maximum="100" Minimum="0" />
<TextBlock Text="{Binding Value, ElementName=slider, Converter={StaticResource DoubleConvert}}" />
</StackPanel>
Define this converter as a resource in your parent view
<UserControl.Resources>
<local:double2String x:Key="convertDouble" />
</UserControl.Resources>
And add it to the binding
<TextBlock x:Name="GaugeBarPercent" Text="{Binding, Converter={StaticResource convertDouble}}"
Don't forget to import the namespace where the converter is defined to your view
xmlns:local="clr-namespace:YOUR_NAMESPACE"
The easier way is to use StringFormat. Like this:
<Label Text="{Binding Path=SomeProperty, StringFormat='{0:F2}%' }"/>

WPF Image Cropping and Binding

I have a simple grid that displays a portion of an image.
<Grid x:Name="back_side" Margin="-1" RenderTransformOrigin="0.5,0.5" RenderTransform="{Binding ScaleTransformBack}" Width="Auto">
<Image Source="/NameSpace;component/Resources/image.jpg" Stretch="Fill">
<Image.Clip>
<RectangleGeometry Rect="{Binding RectGeo}"/>
</Image.Clip>
</Image>
</Grid>
I have tried binding directly to a RectangleGeometry in code behind as well. The clip doesn't seem to want to work. Any suggestions? Anyone have any experience with binding a clip to an image?
I need to be able to programmatically segment a specific image across a number of controls. Using the clip as the calculated portion for each control to display.
If you want to display only part of your image you can use CroppedBitmap as Image.Source
<Image>
<Image.Source>
<CroppedBitmap Source="/NameSpace;component/Resources/image.jpg" SourceRect="{Binding RectGeo}"/>
</Image.Source>
</Image>
You can bind CroppedBitmap.SourceRect but you need to make sure that RectGeo is of a Int32Rect type
EDIT
Unfortunately if you plan to change SourceRect at runtime this won't work as:
After initialization, property changes are ignored
So what you can do is create custom IValueConverter which will create a CroppedBitmap:
public class CropBitmapConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new CroppedBitmap(new BitmapImage(new Uri((string)parameter, UriKind.RelativeOrAbsolute)), (Int32Rect)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and use it with your binding:
<Image ... Source="{Binding Path=RectGeo, Converter={StaticResource CropBitmapConverter}, ConverterParameter='pack://application:,,,/NameSpace;component/Resources/image.jpg'}" />

Categories

Resources