Array Binding simple example doesn't work - c#

I'm very new to xaml and I'm trying to understand the Binding issue. I have a problem with the Array Binding. I created this very simple example: I have a stack panel with three images. Each image has a RotationTransform. Each Angle is obtained by an array element (the array is a DependencyProperty called Rotations). This is the xaml simple file:
<StackPanel Orientation="Vertical">
<Image Source="/Assets/knife.png" Width="50" Height="100" Stretch="Uniform" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding ElementName=pageRoot, Path=Rotations[0], Mode=OneWay}"/>
</Image.RenderTransform>
</Image>
<Image Source="/Assets/fork.png" Width="50" Height="100" Stretch="Uniform" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding ElementName=pageRoot, Path=Rotations[1], Mode=OneWay}"/>
</Image.RenderTransform>
</Image>
<Image Source="/Assets/spoon.png" Width="50" Height="100" Stretch="Uniform" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding ElementName=pageRoot, Path=Rotations[2], Mode=OneWay}"/>
</Image.RenderTransform>
</Image>
<Button x:Name="actionButton" Content="Try Binding!"
Click="Op_Click"/>
</StackPanel>
And this is my c# class:
public sealed partial class MainPage : Page
{
public static readonly DependencyProperty RotationsProperty = DependencyProperty.Register("Rotations", typeof(double[]), typeof(MainPage),
new PropertyMetadata(new double[3]));
public double[] Rotations
{
get { return (double[])GetValue(RotationsProperty); }
set { SetValue(RotationsProperty, value); }
}
private void Op_Click(object sender, RoutedEventArgs e)
{
Rotations[0] = 180;
Rotations[1] = 130;
Rotations[2] = 350;
}
public MainPage()
{
this.InitializeComponent();
Rotations[0] = 20;
Rotations[1] = 90;
Rotations[2] = 180;
}
}
The binding works only the first time (at startup time). When I click on the button (changing the Rotations array) the binding doesn't work and it is completely ignored from my images.
This is a very simple example, so it's clear that I miss something concerning the Binding issue.

I guess the problem is that you change the value of the array entries, but you don't change the array itself, so 'SetValue' does not get called. You could try to set 'Rotations' to a new array.
this.Rotations = new double[] {180, 130, 350};
Edit: I tested your code with my changes and it worked. Another suggestion would be to write a setter method for the array entries and call 'SetValue' or (like suggested in the comments) use 'INotifyPropertyChanged' instead of a DependencyProperty.

Try the below code:
Xaml:
<StackPanel Orientation="Vertical">
<Image Source="/Assets/knife.png" Width="50" Height="100" Stretch="Uniform" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding Rotations[0], Mode=OneWay}"/>
</Image.RenderTransform>
</Image>
<Image Source="/Assets/fork.png" Width="50" Height="100" Stretch="Uniform" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding Rotations[1], Mode=OneWay}"/>
</Image.RenderTransform>
</Image>
<Image Source="/Assets/spoon.png" Width="50" Height="100" Stretch="Uniform" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="{Binding Rotations[2], Mode=OneWay}"/>
</Image.RenderTransform>
</Image>
<Button x:Name="actionButton" Content="Try Binding!"
Click="Op_Click"/>
</StackPanel>
In the code behind set the DataContext as below :
this.DataContext = this;

Ok, I found a solution using ObservableCollection: the xaml file remains the same, but the c# class changes this way:
public sealed partial class MainPage : Page
{
public static readonly DependencyProperty RotationsProperty =
DependencyProperty.Register("Rotations", typeof(ObservableCollection<double>), typeof(MainPage),
new PropertyMetadata(new ObservableCollection<double>()));
public ObservableCollection<double> Rotations
{
get { return (ObservableCollection<double>)GetValue(RotationsProperty); }
set { SetValue(RotationsProperty, value); }
}
private void Op_Click(object sender, RoutedEventArgs e)
{
Rotations[0] = 180;
Rotations[1] = 180;
Rotations[2] = 320;
}
public MainPage()
{
this.InitializeComponent();
Rotations.Add(90);
Rotations.Add(90);
Rotations.Add(90);
}
}

Related

VisualLineElementGenerator VerticalAlignment to Center

In my application I'm using the TextEditor from Avalon.
With the following code I'm creating a VisualLineElementGenerator which works just fine:
internal class SoftwareDependencyElementGenerator : VisualLineElementGenerator
{
private static readonly Regex imageRegex = new Regex(#"<Dependencies>([ \t])*$");
private readonly Action<object> doImportAction;
public SoftwareDependencyElementGenerator(Action<object> doImportAction)
{
this.doImportAction = doImportAction;
}
private Match FindMatch(int startOffset)
{
int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
TextDocument document = CurrentContext.Document;
string relevantText = document.GetText(startOffset, endOffset - startOffset);
return imageRegex.Match(relevantText);
}
public override int GetFirstInterestedOffset(int startOffset)
{
Match match = FindMatch(startOffset);
return match.Success ? (startOffset + match.Index) : -1;
}
public override VisualLineElement ConstructElement(int offset)
{
Match match = FindMatch(offset);
if (match.Success && match.Index == 0)
{
return new InlineObjectElement(0, new AddSoftwareDependencyScriptControl(doImportAction));
}
return null;
}
}
The AddSoftwareDependencyScriptControl which is created in the ConstructElement method looks like:
<UserControl x:Class="MyApplication.AddSoftwareDependencyScriptControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Width="16" Height="16" >
<Grid>
<Button Name="btn" BorderBrush="Transparent" BorderThickness="0" Command="{Binding ShowSoftwareDependenciesCommand}" Width="16" Height="16">
<Button.Content>
<Grid>
<Image Width="14" Height="14" Cursor="Hand" ToolTip="Softwareabhängigkeit hinzufügen"
Source="pack://application:,,,/Resources;component/Graphics/Dependency.png"/>
</Grid>
</Button.Content>
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</UserControl>
To add the SoftwareDependencyElementGererator to the Avalon-TextEditor I just use:
SoftwareDependencyElementGenerator softwareDependencyElementGenerator = new SoftwareDependencyElementGenerator(SelectSoftwareDependency);
AvalonTextEditor.TextArea.TextView.ElementGenerators.Add(softwareDependencyElementGenerator);
Everything just works like expected. But the location of the Control is not where I want it to be.
As you can see. The control is not in the vertical center. I just tried setting the VerticalAlignment of the UserControl, the Button and the Image. Nothing worked. Also making the Image smaller doesn't affect the vertical position.
What can I do to set the Control centered, so it's exactly in one line with the text behind?
I solved it by myself.
I always tried changing the Margin at the top like
Margin="0,10,0,0" but the solution is to make the margin at the bottom negative.
My solution now is:
Margin="0,0,0,-18"

WPF sending value to code behind from dynamic image?

I'm new to using WPF forms, I have created a page that displays images from a database, but despite days of searching I cannot find a way of knowing which image has had a mouse over or mouse click event on it.
To setup the images I have:
public class RBimageData
{
private string _Title;
public string Title
{
get { return this._Title; }
set { this._Title = value; }
}
private BitmapImage _ImageData;
public BitmapImage ImageData
{
get { return this._ImageData; }
set { this._ImageData = value; }
}
private String _ImageID;
public String ImageID
{
get { return this._ImageID; }
set { this._ImageID = value; }
}
}
public MainWindow()
{
InitializeComponent();
RBpartsList rbPartsList = mongoDB.GetRBparts("elements", 1, 7); // get parts from database
List<RBpartsImages> rbImages = rbPartsList.RBparts;
List<RBimageData> md = new List<RBimageData>();
foreach (RBpartsImages img in rbImages)
{
RBimageData m = new RBimageData
{
Title = img.ImageFilename,
ImageID = "id_"+img.PartNum,
ImageData = LoadImage(rbPartsList.FilePath,img.ImageFilename) }; // provides BitmapImage URI for image
md.Add(m);
}
RBbox.ItemsSource = md.ToArray();
}
and the images are displayed in the XAML, I have used the Tag element to hold the ImageID:
<ListView x:Name="RBbox" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Height="143" Margin="10,0,10,10" Background="#FFE6E2E2">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="7" Rows="1" HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border BorderThickness="1" BorderBrush="#FF000000" VerticalAlignment="Top" HorizontalAlignment="Left" Width="100" Height="100" Background="#FFC1A0A0">
<Button
MouseEnter="IdentifyPartImage_MouseEnter"
MouseLeave="IdentifyPartImage_MouseLeave" >
<Image Source="{Binding ImageData}"
HorizontalAlignment="Stretch" VerticalAlignment="Top"
Stretch="UniformToFill"
Tag="{Binding ImageID}"/>
</Button>
</Border>
<TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="100" Height="14" FontSize="10" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
but in my codebehind the this.Tag is always null:
private void IdentifyPartImage_MouseEnter(object sender, MouseEventArgs e)
{
// this fails - tag is null
var imgId = this.Tag.ToString();
Debug.WriteLine("id: {0}, {1}", "imageID", imgId.ToString());
}
It won't work with x:Name="{Binding ImageID}".. I can't find anything that will let me identify which image has been clicked, can you help?
Thanks.
When you reference this in your code-behind, it points to the Window object. You are looking for the Tag property of the Image control.
For that I recommend defining the MouseEnter="IdentifyPartImage_MouseEnter" and MouseLeave="IdentifyPartImage_MouseLeave" events on the Image control, and then the sender parameter will be that Image object.
In your XAML:
<Button>
<Image Source="{Binding ImageData}"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Stretch="UniformToFill"
Tag="{Binding ImageID}"
MouseEnter="IdentifyPartImage_MouseEnter"
MouseLeave="IdentifyPartImage_MouseLeave"/>
</Button>
And in your code-behind:
private void IdentifyPartImage_MouseEnter(object sender, MouseEventArgs e)
{
var imgId = ((Image)sender).Tag.ToString();
Debug.WriteLine("id: {0}, {1}", "imageID", imgId);
}

Custom Window resizing in WPF with a border that previews the size change

I'm having some trouble doind a custom window resizing behavior. What I need is when you resize the window(dragging from the border) it has to show a resizing border that changes its size according to the mouse position and when the mouse left click is released it applies the new size to the window, like Skype does.
What I have is this:
Window XAML:
<Window x:Class="View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:sw="clr-namespace:View.WindowStyle"
Title="CustomWindow" Height="600" Width="800"
MinWidth="800"
MinHeight="600"
WindowStartupLocation="CenterScreen">
<Grid>
<!-- windows items -->
<Thumb x:Name="ThumbTop" Height="6" Margin="8,0" VerticalAlignment="Top" Cursor="SizeNS" Opacity="0" sw:WindowResizeBehavior.TopResize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Thumb x:Name="ThumbTopLeft" Height="8" Width="8" VerticalAlignment="Top" HorizontalAlignment="Left" Cursor="SizeNWSE" Opacity="0" sw:WindowResizeBehavior.TopLeftResize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Thumb x:Name="ThumbBottom" Height="6" Margin="8,0" VerticalAlignment="Bottom" Cursor="SizeNS" Opacity="0" sw:WindowResizeBehavior.BottomResize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Thumb x:Name="ThumbTopRight" Height="8" Width="8" VerticalAlignment="Top" HorizontalAlignment="Right" Cursor="SizeNESW" Opacity="0" sw:WindowResizeBehavior.TopRightResize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Thumb x:Name="ThumbRight" HorizontalAlignment="Right" Margin="0,6" Width="6" Cursor="SizeWE" Opacity="0" sw:WindowResizeBehavior.RightResize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Thumb x:Name="ThumbBottomRight" Height="8" Width="8" VerticalAlignment="Bottom" HorizontalAlignment="Right" Cursor="SizeNWSE" Opacity="0" sw:WindowResizeBehavior.BottomRightResize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Thumb x:Name="ThumbLeft" HorizontalAlignment="Left" Margin="0,8" Width="6" Cursor="SizeWE" Opacity="0" sw:WindowResizeBehavior.LeftResize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Thumb x:Name="ThumbBottomLeft" Height="8" Width="8" VerticalAlignment="Bottom" HorizontalAlignment="Left" Cursor="SizeNESW" Opacity="0" sw:WindowResizeBehavior.BottomLeftResize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
</Grid>
</Window>
I'm using 8 thumbs to handle the resizing behavior and this is handled in a class, this is how the resize is done (just for one thumb but its the same logic for the other 7):
public static class WindowResizeBehavior
{
public static Window GetBottomRightResize(DependencyObject obj)
{
return (Window)obj.GetValue(BottomRightResize);
}
public static void SetBottomRightResize(DependencyObject obj, Window window)
{
obj.SetValue(BottomRightResize, window);
}
public static readonly DependencyProperty BottomRightResize = DependencyProperty.RegisterAttached("BottomRightResize",
typeof(Window), typeof(WindowResizeBehavior),
new UIPropertyMetadata(null, OnBottomRightResizeChanged));
private static void OnBottomRightResizeChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var thumb = sender as Thumb;
if (thumb != null)
{
thumb.DragCompleted += DragBottomRight;
}
}
private static void DragBottomRight(object sender, DragCompletedEventArgs e)
{
var thumb = sender as Thumb;
var window = thumb.GetValue(BottomRightResize) as Window;
if (window != null)
{
var verticalChange = window.SafeHeightChange(e.VerticalChange);
var horizontalChange = window.SafeWidthChange(e.HorizontalChange);
window.Width += horizontalChange;
window.Height += verticalChange;
}
}
private static double SafeWidthChange(this Window window, double change, bool positive = true)
{
var result = positive ? window.Width + change : window.Width - change;
if (result <= window.MinWidth)
{
if (positive)
return window.MinWidth - window.Width;
else
return window.Width - window.MinWidth;
} else if(result >= window.MaxWidth)
{
return 0;
} else if(result < 0)
{
return 0;
}
else
{
return change;
}
}
private static double SafeHeightChange(this Window window, double change, bool positive = true)
{
var result = positive ? window.Height + change : window.Height - change;
if (result <= window.MinHeight)
{
if (positive)
return window.MinHeight - window.Height;
else
return window.Height - window.MinHeight;
}
else if (result >= window.MaxHeight)
{
return 0;
}
else if (result < 0)
{
return 0;
}
else
{
return change;
}
}
}
With this the resize is done when the mouse is release but now I don't know how to add the resizable border. By the way I'm using .NET 4.5 with MVVM.
Many thanks
<Window WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
ResizeMode="CanResize"
Title="WareHouse"
Height="600"
Width="1000"
>
<WindowChrome.WindowChrome>
<WindowChrome/>
</WindowChrome.WindowChrome>
</Window>
WindowsChrome will let you resize the custom window.

How to disable "kinetic energy" of the UI object (UWP)

I have Image on View that I can drag. But when I release the object it is moving for some time. How I can disable this?
My Xaml code:
<Image Grid.Column="0"
x:Name="CollageImg1"
Margin="370,469,1665,800"
Source="{Binding CollageImg1}"
RenderTransformOrigin="0.5,0.5"
ManipulationDelta="CollageImgage1_Manipulation"
Visibility="Visible">
<Image.RenderTransform>
<CompositeTransform />
</Image.RenderTransform>
</Image>
Method that uses for moving:
private void Manipulation(ManipulationDeltaRoutedEventArgs e, Image image)
{
CompositeTransform ct = (CompositeTransform)image.RenderTransform;
ct.TranslateX += e.Delta.Translation.X;// X move
ct.TranslateY += e.Delta.Translation.Y;// Ymove
}
Remove TranslateInertia from the ManipulationMode property of the Image.
E.g. set only TranslateX and TranslateY:
<Image ManipulationMode="TranslateX,TranslateY" ... />

silverlight 2 binding data to transforms?

I am working on creating a tag cloud in Silverlight 2 and trying to bind data from a List collection to a Scale transform on a TextBlock. When running this I get an AG_E_PARSER_BAD_PROPERTY_VALUE error. Is it possible to data bind values to transforms in Silverlight 2? If not could I do something to the effect of FontSize={Binding Weight*18} to multiply the tag's weight by a base font size? I know this won't work, but what is the best way to calculate property values for items in a DataTemplate?
<DataTemplate>
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch" TextWrapping="Wrap" d:IsStaticText="False" Text="{Binding Path=Text}" Foreground="#FF1151A8" FontSize="18" UseLayoutRounding="False" Margin="4,4,4,4" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="{Binding Path=WeightPlusOne}" ScaleY="{Binding Path=WeightPlusOne}"/>
</TransformGroup>
</TextBlock.RenderTransform>
The issue seems to be Rule #1 from this post:
The target of data binding must be a FrameworkElement.
So since ScaleTransform isn't a FrameworkElement it doesn't support binding. I tried to bind to a SolidColorBrush to test this out and got the same error as with the ScaleTransform.
So in order to get around this you can create a control that exposes a dependency property of your tag data type. Then have a property changed event that binds the properties of your tag data to the properties in the control (one of which would be the scale transform). Here is the code I used to test this out.
items control:
<ItemsControl x:Name="items">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:TagControl TagData="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
tag control xaml:
<UserControl x:Class="SilverlightTesting.TagControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<TextBlock x:Name="text" TextWrapping="Wrap" FontSize="18" Margin="4,4,4,4">
<TextBlock.RenderTransform>
<ScaleTransform x:Name="scaleTx" />
</TextBlock.RenderTransform>
</TextBlock>
</UserControl>
tag control code:
public partial class TagControl : UserControl
{
public TagControl()
{
InitializeComponent();
}
public Tag TagData
{
get { return (Tag)GetValue(TagDataProperty); }
set { SetValue(TagDataProperty, value); }
}
// Using a DependencyProperty as the backing store for TagData. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TagDataProperty =
DependencyProperty.Register("TagData", typeof(Tag), typeof(TagControl), new PropertyMetadata(new PropertyChangedCallback(TagControl.OnTagDataPropertyChanged)));
public static void OnTagDataPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var tc = obj as TagControl;
if (tc != null) tc.UpdateTagData();
}
public void UpdateTagData()
{
text.Text = TagData.Title;
scaleTx.ScaleX = scaleTx.ScaleY = TagData.Weight;
this.InvalidateMeasure();
}
}
Seems like overkill for just setting a single property, but I couldn't find an easier way.

Categories

Resources