I have got an usercontrol with some dependency property. One of them (ValueProperty) has got a PropertyChangedCallback but it never run.
namespace test
{
public partial class IndicatorLigth : UserControl
{
public IndicatorLigth()
{
InitializeComponent();
DataContext = this;
CurrentBrush = new SolidColorBrush(InactiveColor);
lIndicator.Background = CurrentBrush;
TurnOnValue = true;
Value = true;
}
public static readonly DependencyProperty ActiveColorProperty =
DependencyProperty.Register("ActiveColor", typeof(Color), typeof(IndicatorLigth), new UIPropertyMetadata(Colors.Green));
public Color ActiveColor
{
get { return (Color)GetValue(ActiveColorProperty); }
set { SetValue(ActiveColorProperty, value); }
}
public static readonly DependencyProperty InactiveColorProperty =
DependencyProperty.Register("InactiveColor", typeof(Color), typeof(IndicatorLigth), new UIPropertyMetadata(Colors.Red));
public Color InactiveColor
{
get { return (Color)GetValue(InactiveColorProperty); }
set { SetValue(InactiveColorProperty, value); }
}
private SolidColorBrush _currentBrush;
public SolidColorBrush CurrentBrush
{
get { return _currentBrush; }
set { _currentBrush = value; }
}
public static readonly DependencyProperty TurnOnValueProperty =
DependencyProperty.Register("TurnOnValue", typeof(bool), typeof(IndicatorLigth), new UIPropertyMetadata(true));
public bool TurnOnValue
{
get { return (bool)GetValue(TurnOnValueProperty); }
set { SetValue(TurnOnValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(bool), typeof(IndicatorLigth),
new FrameworkPropertyMetadata(true,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSetColorChanged));
public bool Value
{
get { return (bool)GetValue(ValueProperty); }
set
{
SetValue(ValueProperty, value);
}
}
private void CheckStatus(bool sign)
{
if (sign == TurnOnValue)
CurrentBrush = new SolidColorBrush(ActiveColor);
else CurrentBrush = new SolidColorBrush(InactiveColor);
}
private static void OnSetColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
IndicatorLigth mycontrol = d as IndicatorLigth;
mycontrol.callmyInstanceMethod(e);
}
private void callmyInstanceMethod(DependencyPropertyChangedEventArgs e)
{
CheckStatus((bool)e.NewValue);
lIndicator.Background = CurrentBrush;
}
}
}
And XAML where I use my usercontrol (I use it in another UserControl):
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:test"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
...
<StackPanel Orientation="Vertical">
<Label Content="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.Sign}"/>
<StackPanel>
<local:IndicatorLigth ActiveColor="Thistle" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.Sign}"/>
</StackPanel>
</StackPanel>
The Sign parameter belongs to an IsEnabled bindable property of a ComboBox which not in the XAML code. The label content is correct, it changes when I change combobox enabled status, but my UserControl setter of Value, OnSetColorChanged and callmyInstanceMethod don't fire. Could you tell me what wrong in my code? Thank you very much.
Update: So I was wrong. The code mentioned above is correct. The problem will be occures when I push the stackpanel into a devexpress LayoutGroup HeaderTemplate:
<dxlc:LayoutGroup Orientation="Vertical" VerticalAlignment="Top">
<dxlc:LayoutGroup.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Label Content="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.Sign}"/>
<StackPanel>
<local:IndicatorLigth ActiveColor="Thistle" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.Sign}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</dxlc:LayoutGroup.HeaderTemplate>
</dxlc:LayoutGroup>
Sorry for disturbing you and thank you for advices. I have found the cause of problem. I needn't use <HeaderTemplate><DataTemplate>, I have to use simple <Header> block :)
Related
I have a simple Window with a TextBox
XAML
<Window x:Class="Configurator.ConfiguratorWindow"
x:Name="ConfigWindow" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox x:Name="DescriptionTextBox" Text="{Binding Path=Description, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
</Window>
in the code behind
public partial class ConfiguratorWindow : Window
{
public ConfiguratorWindow()
{
InitializeComponent();
}
private static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(ConfiguratorWindow), new PropertyMetadata());
public string Description
{
get { return GetValue(DescriptionProperty).ToString(); }
set {
SetValue(DescriptionProperty, value);
_actual_monitor.Description = value;
}
}
}
the graphic is updating right, but when i change the text in the textbox and lose focus it doesn't update the source property.
What is wrong?
DependencyProperties are used for UserControls rather than ViewModel type bindings.
You should
Create a ConfigurationWindowViewModel (Read about MVVM) and implement INotifyPropertyChanged
Create a Property Description that utilizes the INotifyPropertyChanged
Create a new instance of that view model to be set to the DataContext of your ConfigurationWindow.
The getter and setter of the CLR wrapper of a dependency property must not contain any other code than GetValue and SetValue. The reason is explained in the XAML Loading and Dependency Properties article on MSDN.
So remove the _actual_monitor.Description = value; assignment from the setter and add a PropertyChangedCallback to react on property value changes:
public partial class ConfiguratorWindow : Window
{
public ConfiguratorWindow()
{
InitializeComponent();
}
private static DependencyProperty DescriptionProperty = DependencyProperty.Register(
"Description", typeof(string), typeof(ConfiguratorWindow),
new PropertyMetadata(DescriptionPropertyChanged));
public string Description
{
get { return (string)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
private static void DescriptionPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ConfiguratorWindow obj = d as ConfiguratorWindow;
obj._actual_monitor.Text = (string)e.newValue;
}
}
Try this
<Window x:Class="Configurator.ConfiguratorWindow"
xmlns:myWindow="clr-namespace:YourNamespace"
x:Name="ConfigWindow" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox x:Name="DescriptionTextBox" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type myWindow}}, Path=Description, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
public partial class ConfiguratorWindow : Window
{
public ConfiguratorWindow()
{
InitializeComponent();
}
private static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(ConfiguratorWindow), new PropertyMetadata(null, CallBack);
private static void callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var foo = d as ConfiguratorWindow ;
all you need to do, you can do here
}
public string Description
{
get { return GetValue(DescriptionProperty).ToString(); }
set { SetValue(DescriptionProperty, value);}
}
}
But it would be much easier to just have a View Model and bind to property there.
Preface
The control I am giving as an example is an sample work for a larger project. I have already had some help from the community on Stackoverflow ironing out some of the finer points of bindings within the control. The surprise has been that I am having an issue binding in the control's hosting form.
I have read and researched around DependencyProperty for a lot of hours. I was not a WPF developer at the start of the year but I am now covering the role because of a death in the business, and I accept this is a big hill to climb.
The question is what is missing here in my:
The hosting form's XAML code
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:AControl="clr-namespace:AControl;assembly=AControl" x:Class="DependencySampler.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, Mode=OneWayToSource}"/>
</Grid>
The code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new viewModelBinding();
BeSelected = new modelMain("Yellow", "#FFFFE0");
}
public modelMain BeSelected
{
get { return ((viewModelBinding)DataContext).Selected; }
set { ((viewModelBinding)DataContext).Selected = value; }
}
}
The ViewModel
public class viewModelBinding :ViewModelBase
{
modelMain sel = new modelMain("Red", "#FF0000");
public modelMain Selected
{
get { return sel; }
set { SetProperty(ref this.sel, value, "Selected"); }
}
}
The next section is the control itself.
The Model
public class modelMain:ViewModelBase
{
public modelMain(string colName, string hexval)
{
ColorName = colName;
HexValue = hexval;
}
string colorName;
public string ColorName
{
get { return colorName; }
set { SetProperty(ref this.colorName, value, "ColorName"); }
}
string hexValue;
public string HexValue
{
get { return hexValue; }
set { SetProperty(ref this.hexValue, value, "HexValue"); }
}
}
The ViewModel
public class viewModelMain:ViewModelBase
{
ObservableCollection<modelMain> val = new ObservableCollection<modelMain>();
public ObservableCollection<modelMain> ColorsList
{
get { return val; }
set { SetProperty(ref this.val, value, "Colors"); }
}
modelMain selectedColor;
public modelMain SelectedColour
{
get{return selectedColor;}
set { SetProperty(ref this.selectedColor, value, "SelectedColour"); }
}
public void SetCurrentColor(modelMain col)
{
SelectedColour = this.val.Where(x => x.ColorName == col.ColorName).FirstOrDefault();
}
public viewModelMain()
{
val.Add(new modelMain("Red", "#FF0000"));
val.Add(new modelMain("Blue", "#0000FF"));
val.Add(new modelMain("Green", "#008000"));
val.Add(new modelMain("Yellow", "#FFFFE0"));
SelectedColour = new modelMain("Blue", "#0000FF");
}
}
The UserControl XAML
<UserControl x:Class="AControl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="32" d:DesignWidth="190">
<Grid>
<ComboBox x:Name="cboValue"
SelectionChanged="cboValue_SelectionChanged"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding ColorList, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedValue="{Binding SelectedColor, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="10"
Height="10"
Margin="5"
Background="{Binding ColorName}"/>
<TextBlock Width="35"
Height="15"
Margin="5"
Text="{Binding ColorName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
The UserControl Code behind
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
ObservableCollection<modelMain> colorList = new viewModelMain().ColorsList;
public ObservableCollection<modelMain> ColorList
{
get { return colorList; }
set { colorList = value; }
}
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(
"SelectedColor",
typeof(modelMain),
typeof(UserControl1),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnSelectedColorChanged),
new CoerceValueCallback(CoerceSelectedColorCallback)));
private static void OnSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 uc = (UserControl1)d;
uc.SelectedColor = (modelMain)e.NewValue;
}
private static object CoerceSelectedColorCallback(DependencyObject d, object value)
{
return (modelMain)value;
}
public modelMain SelectedColor
{
get { return (modelMain)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
private void cboValue_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var dat = sender as ComboBox;
SelectedColor = (modelMain)dat.SelectedValue;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
//var dat = sender as ComboBox;
////SelectedColor = (modelMain)dat.SelectedValue;
//SelectedColor = (modelMain)this.SelectedColor;
}
}
Please note that in the code behind there is unused code but within the sample I have used then for placing break points
I understand that no DataContext should exist in the UserControl because it precludes one in the hosting form.
The Question
I was expecting the this line would be sufficient in the hosting form.
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, Mode=OneWayToSource}"/>
But it does not seem to do what I expected. I can see the BeSelected be initialised and it is holding a value but when the form loads I am expecting the colour yellow to enter the UserControl's and set DependencyProperty SelectedColor. This is not happening why and how can I get it to happen?
To get you example working, do the following (for the most part, implement what the commenters said):
The hosting form's XAML code
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, RelativeSource={RelativeSource AncestorType=Window}}}"/>
The Mode doesn't really matter since MainWindow doesn't implement INPC nor does it ever know when ((viewModelBinding)DataContext).Selected (and therefor, BeSelected) is changed. Actually, like Joe stated, OneWayToSource doesn't work... RelativeSource was needed because BeSelected is a property of the MainWindow - not MainWindow's DataContext.
modelMain
modelMain needs to implement IEquatable (like Janne commented). Why? Because BeSelected = new modelMain(...) creates a new modelMain which is not one of the items in the ComboBox's ItemsSource (ColorList). The new object may have the same property values as one of the items but that doesn't make them equal (different objects = different address in memory). IEquatable gives you the opportunity to override that.
public class modelMain : ViewModelBase, IEquatable<modelMain>
{
...
public bool Equals(modelMain other)
{
return (HexValue == other.HexValue);
}
}
viewModelMain's ColorList's setter is calling SetProperty with property name "Colors" when it should be "ColorsList". It's not being used so it doesn't stop your example from working but it's still wrong.
I'm currently trying to implement some kind of camera-logic for a schoolproject( 2D Multiplayer-PacMan) using WPF(first time im using wpf). We have a Tile-Based Walkmap using a Canvas and an ItemControl, which is bigger than the actual screensize:
GameView.xaml
<controls:Camera HorizontalOffset="{Binding xPos}" VerticalOffset="{Binding yPos}">
<ItemsControl ItemsSource="{Binding Tiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{Binding ImagePath}" Width="{Binding Size}" Height="{Binding Size}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</controls:Camera>
I'm trying to use a ScrollViewer for my Camera, but as you all know we can't bind The Horizontal and Vertical Offset to Properties due to the fact that they are readonly.
Thats why I created a UserControl named "Camera" which has a ScrollViewer and two DependencyProperties for Binding.
Camera.xaml
<UserControl x:Class="PacmanClient.UserControls.Camera"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PacmanClient.UserControls"
mc:Ignorable="d"
d:DesignHeight="1600" d:DesignWidth="1900">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<ScrollViewer Name="cameraViewer" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" IsEnabled="True">
<ContentPresenter/>
</ScrollViewer>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Camera.xaml.cs
public partial class Camera : UserControl
{
ScrollViewer cameraViewer;
public Camera()
{
InitializeComponent();
}
#region HorizontalOffset
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
cameraViewer = this.Template.FindName("cameraViewer", this) as ScrollViewer;
}
public double HorizontalOffset
{
get
{
return (double)GetValue(HorizontalOffsetProperty);
}
set
{
SetValue(HorizontalOffsetProperty, value);
OnHorizontalOffsetChanged(value);
}
}
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.RegisterAttached("HorizontalOffset", typeof(double), typeof(Camera),
new UIPropertyMetadata(0.0));
private void OnHorizontalOffsetChanged(double value)
{
cameraViewer.ScrollToHorizontalOffset(value);
}
#endregion
#region VerticalOffset
public double VerticalOffset
{
get
{
return (double)GetValue(VerticalOffsetProperty);
}
set
{
SetValue(VerticalOffsetProperty, value);
OnVerticalOffsetChanged(value);
}
}
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), typeof(Camera),
new UIPropertyMetadata(0.0));
private void OnVerticalOffsetChanged(double value)
{
cameraViewer.ScrollToVerticalOffset(value);
}
#endregion
}
Now I'm having two problems.
First:
When im trying to use my UserControl(as seen in GameView.xaml) and bind some Properties to the DependencyProperties, I'm getting the Error that those members are not recognized or accessible.( I actually thought i fixed this, but now it's back.) This has to be an AccessProblem, because autocompletion actually suggests me HorinzontalOffset and VerticalOffset.
I just can't find a solution.
And Second:
In the Version where I was able to access those Properties and successfully bind some Properties to them, the Values of the DependencyProperties never changed when the properties bound to them changed. I checked it via debugging and the setter of the code behind property is never called.
I'm hoping you can help me with those problems, i have no idea why it isn't working.
[Edit]
MainWindow.Xaml
<Window x:Class="WpfApplication3.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:WpfApplication3"
xmlns:test="clr-namespace:test"
mc:Ignorable="d"
Title="MainWindow" Height="1600" Width="1900">
<StackPanel>
<Button Content="yolo" Click="Button_Click"></Button>
<ScrollViewer Name ="hallo" Height="1600" Width="1600" test:ScrollViewerExtension.HorizontalOffset = "{Binding xPos}" test:ScrollViewerExtension.VerticalOffset="{Binding yPos}" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
<Canvas Height="3000" Width="3000">
<Ellipse Name="e1" Height="42" Width="42" Fill="Yellow"></Ellipse>
</Canvas>
</ScrollViewer>
</StackPanel>
</Window>
Mainwindow.cs
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public double xPos
{
get;
set;
}
public double yPos
{
get;
set;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
xPos += 50.0;
yPos += 50.0;
Canvas.SetTop(e1, yPos);
Canvas.SetLeft(e1, xPos);
}
}
}
If you craete custom control, you should create DependencyProperty. You created AttachedProperty, that's something else.
I will show you, how to use attached property:
<ScrollViewer x:Name="ScrollViewer1" Height="100" Width="150"
HorizontalScrollBarVisibility="Auto"
local:ScrollViewerExtension.HorizontalOffset="{Binding Value, ElementName=Slider1}">
<Rectangle Height="80" Width="100" Margin="100,50,0,0" Fill="Red"/>
</ScrollViewer>
<!-- you can databind ScrollViewerExtension.HorizontalOffset to whatever,
Slider is just for demonstration purposes -->
<Slider x:Name="Slider1"
Maximum="{Binding ElementName=ScrollViewer1, Path=ScrollableWidth}" />
and the attached property definition:
public static class ScrollViewerExtension
{
public static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.RegisterAttached("HorizontalOffset", typeof (double), typeof (ScrollViewerExtension),
new PropertyMetadata(HorizontalOffsetChanged));
private static void HorizontalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var scrollViewer = (ScrollViewer) d;
scrollViewer.ScrollToHorizontalOffset((double)e.NewValue);
}
public static void SetHorizontalOffset(DependencyObject element, double value)
{
element.SetValue(HorizontalOffsetProperty, value);
}
public static double GetHorizontalOffset(DependencyObject element)
{
return (double) element.GetValue(HorizontalOffsetProperty);
}
}
as you can see, attached properties should be used with existing controls. If you create new control, use dependency properties
Well, let's see if this helps you. Your first problem is difficult to solve without seeing your full code, because what you have shown here does not have any problem (in fact, i made a test solution copying/pasting this code and it worked). The second problem has to do with how you have defined the attached properties.
In the third parameter of RegisterAttached you should add a callback to your propertyChanged event handling method, like this:
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.RegisterAttached("HorizontalOffset", typeof(object), typeof(Camera),new UIPropertyMetadata(null, HorizontalOffsetPropertyChanged));
Also you need to add two methods for getting/setting this property value:
public static object GetHorizontalOffset(DependencyObject obj)
{
return (string)obj.GetValue(HorizontalOffsetProperty);
}
public static void SetHorizontalOffset(DependencyObject obj, object value)
{
obj.SetValue(HorizontalOffsetProperty, value);
}
This is the full code for your usercontrol:
public partial class Camera : UserControl
{
public Camera()
{
InitializeComponent();
}
#region HorizontalOffset
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.RegisterAttached("HorizontalOffset", typeof(object), typeof(Camera),new UIPropertyMetadata(null, HorizontalOffsetPropertyChanged));
public static object GetHorizontalOffset(DependencyObject obj)
{
return (string)obj.GetValue(HorizontalOffsetProperty);
}
public static void SetHorizontalOffset(DependencyObject obj, object value)
{
obj.SetValue(HorizontalOffsetProperty, value);
}
public static void HorizontalOffsetPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
Camera cam = o as Camera;
ScrollViewer scroll=cam.Template.FindName("cameraViewer", cam) as ScrollViewer;
double horizontal = 0;
if (e.NewValue is double)
{
horizontal =(double) e.NewValue;
}
scroll.ScrollToHorizontalOffset(horizontal);
}
#endregion
#region VerticalOffset
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.RegisterAttached("VerticalOffset", typeof(object), typeof(Camera), new UIPropertyMetadata(null, VerticalOffsetPropertyChanged));
public static object GetVerticalOffset(DependencyObject obj)
{
return (string)obj.GetValue(VerticalOffsetProperty);
}
public static void SetVerticalOffset(DependencyObject obj, object value)
{
obj.SetValue(VerticalOffsetProperty, value);
}
public static void VerticalOffsetPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
Camera cam = o as Camera;
ScrollViewer scroll = cam.Template.FindName("cameraViewer", cam) as ScrollViewer;
double vertical = 0;
if (e.NewValue is double)
{
vertical = (double)e.NewValue;
}
scroll.ScrollToVerticalOffset(vertical);
}
#endregion
}
Hope this gets you on the track
Xaml as below:
<ItemsControl
x:Class="PowersOf2.Windows10.Views.Controls.Board"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PowersOf2.Windows10.Views.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="Root" ItemsSource="{Binding Fields, ElementName=Root}" Loaded="Root_Loaded"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid
Width="{Binding FieldWidth, ElementName=Root}"
Height="{Binding FieldHeight, ElementName=Root}"
Loaded="Grid_Loaded" Background="White"
>
<Grid.RenderTransform>
<TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
</Grid.RenderTransform>
<TextBlock Text="{Binding Text}" Foreground="Black"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Fields is IEnumerable of Field which has coordinates X and Y. They are managed by view model. FieldWidth and FieldHeight are dependency properties calculated in code behind.
How to get binding object of nested dependency properties such as TranslateTransform.X and TranslateTransform.Y in code behind?
UPDATE:
Based on this question: Fredrik's answer works as expected until you work with single embedded object in xaml with binding to non-nested properties, but not for nested ones. This issue is more complicated due to ItemsControl containing my Grid.
Code behind below:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Windows.UI.Xaml;
namespace Controls
{
public sealed partial class Board
{
public Board()
{
InitializeComponent();
}
private void Root_Loaded(object sender, RoutedEventArgs e)
{
FieldWidth = 100.0;
FieldHeight = 100.0;
Fields =
new Field[]
{
new Field { X = 100, Y = 100, Text = "one" },
new Field { X = 300, Y = 300, Text = "two" }
};
}
public double FieldWidth
{
get { return (double)GetValue(FieldWidthProperty); }
set { SetValue(FieldWidthProperty, value); }
}
public static readonly DependencyProperty FieldWidthProperty = DependencyProperty.Register(
"FieldWidth", typeof(double), typeof(Board), new PropertyMetadata(0.0)
);
public double FieldHeight
{
get { return (double)GetValue(FieldHeightProperty); }
set { SetValue(FieldHeightProperty, value); }
}
public static readonly DependencyProperty FieldHeightProperty = DependencyProperty.Register(
"FieldHeight", typeof(double), typeof(Board), new PropertyMetadata(0.0)
);
public IEnumerable<Field> Fields
{
get { return (ObservableCollection<Field>)GetValue(FieldsProperty); }
set { SetValue(FieldsProperty, value); }
}
public static readonly DependencyProperty FieldsProperty = DependencyProperty.Register(
"Fields", typeof(IEnumerable<Field>), typeof(Board), new PropertyMetadata(null)
);
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
// here I want to get binding of RenderTransform's properties
}
}
public class Field : INotifyPropertyChanged
{
private int _x;
public int X
{
get { return _x; }
set
{
_x = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("X"));
}
}
private int _y;
public int Y
{
get { return _y; }
set
{
_y = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Y"));
}
}
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Text"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
I hope I haven't misinterpreted the question but you can get the transformation and the bound item like this.
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
var grid = (Grid)sender;
//the actual transformation
var render = (Transform)grid.GetValue(RenderTransformProperty);
//the field the transformation is bound to
var field = (Field)grid.DataContext;
//for now this only works in WPF
var binding = BindingOperations.GetBinding(render, TranslateTransform.XProperty);
}
Made an edit for this, but it does not work for winrt.
The method BindingOperations.GetBinding is only available in WPF.
Hope that winrt gets this soon.
I would try to create a user control in a windows app. I tried to enter this code:
public double Minimum
{
get { return (double)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
public double LowerValue
{
get { return (double)GetValue(LowerValueProperty); }
set { SetValue(LowerValueProperty, value); }
}
public static readonly DependencyProperty LowerValueProperty =
DependencyProperty.Register("LowerValue", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
public double UpperValue
{
get { return (double)GetValue(UpperValueProperty); }
set { SetValue(UpperValueProperty, value); }
}
public static readonly DependencyProperty UpperValueProperty =
DependencyProperty.Register("UpperValue", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(0d));
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(double), typeof(RangeSlider), new UIPropertyMetadata(1d));
Then I plugged in xaml control so:
<UserControl
x:Class="Doppio_Cursore.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Doppio_Cursore"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
x:name= "root">
<Grid>
<Slider x:Name="LowerSlider"
Minimum="{Binding ElementName=root, Path=Minimum}"
Maximum="{Binding ElementName=root, Path=Maximum}"
Value="{Binding ElementName=root, Path=LowerValue}" />
<Slider x:Name="UpperSlider"
Minimum="{Binding ElementName=root, Path=Minimum}"
Maximum="{Binding ElementName=root, Path=Maximum}"
Value="{Binding ElementName=root, Path=UpperValue}" />
</Grid>
</UserControl>
I believe that with this code if I'm not mistaken, I should see a slider-slider, no?
Instead I get a simple slider with a single slider. Where am I wrong?