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.
Related
I have a listview with values that are being updated constantly from a different thread.
I want to change the color of the background according to the value of the item.
After reading a lot I came to the following conclusions:
The correct way to set background color for list view item is via style selector.
Style selector is called only once in the initialization of the list.
How can I achieve this simple behavior?
xaml:
<Page
x:Class="MyProject.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyProject"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.DataRef.Values, Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ValWrapper">
<TextBlock Text="{x:Bind Val, Mode=OneWay}"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyleSelector>
<local:CustomItemContainerStyleSelector>
<local:CustomItemContainerStyleSelector.Bad>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="Red"/>
</Style>
</local:CustomItemContainerStyleSelector.Bad>
<local:CustomItemContainerStyleSelector.Good>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="Green"/>
</Style>
</local:CustomItemContainerStyleSelector.CloseToBad>
</local:CustomItemContainerStyleSelector>
</ListView.ItemContainerStyleSelector>
</ListView>
</Grid>
</Page>
cs:
public sealed partial class MainPage : Page
{
public ViewModel ViewModel { get; set; }
public MainPage()
{
this.InitializeComponent();
this.ViewModel = new ViewModel();
}
}
public class CustomItemContainerStyleSelector : StyleSelector
{
public Style Bad { get; set; }
public Style Good { get; set; }
protected override Style SelectStyleCore(object item, DependencyObject container)
{
double threshold = 1;
ValWrapper v = (ValWrapper)item;
if (v.Val <= threshold)
{
return Bad;
}
else {
return Good;
}
}
}
Whenever the data changes, "NotifyPropertyChanged" is called (implements INotifyPropertyChanged).
Please check the following steps:
Set a temporary variable _tempValue to record previous number.
Bind the Background property to IsUpdate, the initial value is all false.
If the number changes, please set IsUpdate to true, then the Background of ListViewItem turns red.
XAML:
<Page
x:Class="Permisson.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Permisson"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<local:ColorConverter x:Key="ColorConverter"/>
</Page.Resources>
<Grid>
<StackPanel>
<ListView ItemsSource="{x:Bind ViewModel.DataRef, Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate >
<DataTemplate x:DataType="local:ValWrapper">
<Grid Background="{Binding IsUpdate, Converter={StaticResource ColorConverter},Mode=OneWay}">
<TextBlock Text="{Binding Val, Mode=OneWay}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="ChangeNum" Click="Button_Click"/>
<Button Content="ChangeNum2" Click="Button_Click_1"/>
</StackPanel>
</Grid>
</Page>
Code behind:
namespace Permisson
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public ViewModel ViewModel { get; set; }
public MainPage()
{
this.InitializeComponent();
this.ViewModel = new ViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var v = ViewModel.DataRef[0];
v.Val = 9;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var v = ViewModel.DataRef[1];
v.Val = 10;
}
}
public class ViewModel
{
private ObservableCollection<ValWrapper> dataRef = new ObservableCollection<ValWrapper>()
{
new ValWrapper {Val=22,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false },
new ValWrapper {Val=25,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false},
new ValWrapper {Val=35,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false},
new ValWrapper {Val=45,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false },
new ValWrapper {Val=55,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false},
new ValWrapper {Val=65,Brush=new SolidColorBrush (Colors.Green),IsUpdate = false }
};
public ObservableCollection<ValWrapper> DataRef { get { return dataRef; } }
}
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var color = new SolidColorBrush();
if ((bool)value)
{
color.Color = Colors.Red;
}
else
{
color.Color = Colors.Green;
}
return color;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
public class ValWrapper : INotifyPropertyChanged
{
private int val;
private SolidColorBrush brush;
public SolidColorBrush Brush
{
get { return brush; }
set
{
brush = value;
RaisePropertyChanged();
}
}
private int _tempValue;
public int Val
{
get { return val; }
set
{
if(_tempValue != value && _tempValue != 0)
{
IsUpdate = true;
}
val = value;
RaisePropertyChanged();
_tempValue = val;
}
}
private bool _isUpdate;
public bool IsUpdate
{
set
{
_isUpdate = value;
RaisePropertyChanged();
}
get
{
return _isUpdate;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyname));
}
}
}
}
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.
Scenario
I have Two Classes:
Fruit has two data members FruitName and FruitColor
Presentation also has two members ForeColorand FontName
A ListBox's ItemsSource property is set to List<Fruit>. And the fruits are listed.
However, I want to change FontFamily and Foreground of the TextBlock control, which should reflect immediately as I set the Presentation instance to the ListBox.
Problem
The problem is that
When I am calling the btnChangeColor_Click(), I receive an exception "Object reference not set to an instance of an object". In the INotifyPropertyChanged() method. Exactly here...
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
I want to set ListBox with some Fruits object showing FruitName and FruitColor. I also want to change font names and the foreground of the TextBlock showing the FruitName and the FruitColor, so that changing the color and font name should reflect immediately
Code
TestingRealTimeUIUpdate.xaml
<Page
x:Class="dataStorage_And_AppSettings.TestingRealTimeUIUpdate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:dataStorage_And_AppSettings"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<ListBox x:Name="lstFruits" Height="400" Background="Aqua">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="lblFruitName" Text="{Binding Fruits.FruitName}" Foreground="{Binding Presentations.ForeColor}" FontFamily="{Binding Presentations.FontName}" />
<TextBlock x:Name="lblFruitColor" Text="{Binding Fruits.FruitColor}" Foreground="{Binding Presentations.ForeColor}" FontFamily="{Binding Presentations.FontName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="lstColors" Height="175" Background="Goldenrod" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="lblFruitName" Text="FontColors And Font Name" Foreground="{Binding Presentation.ForeColor}" FontFamily="{Binding Presentation.FontName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="btnReloadFruits" Content="ReloadBasket" Click="btnReload_Click" />
<Button x:Name="btnChangeColor" Content="ChangeColor" Click="btnChangeColor_Click" />
</StackPanel>
</Grid>
</Page>
The TestingRealTimeUIUpdate.CS
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace dataStorage_And_AppSettings
{
public sealed partial class TestingRealTimeUIUpdate : Page
{
private Comp FruitBasket;
private List<Presentation> PresentationForFruitBasket = new List<Presentation>
{
new Presentation { FontName = "Arial", ForeColor = new SolidColorBrush(Colors.Green) },
new Presentation { FontName = "Verdana", ForeColor = new SolidColorBrush(Colors.Yellow) },
new Presentation { FontName = "Times New roman", ForeColor = new SolidColorBrush(Colors.Brown) },
new Presentation { FontName = "Tahoma", ForeColor = new SolidColorBrush(Colors.Red) },
};
private List<Fruit> FruitForFruitBasket = new List<Fruit>
{
new Fruit { FruitName= "Mango", FruitColor="Yellow" },
new Fruit {FruitName = "Banana", FruitColor= "Yellow" },
new Fruit { FruitName="Grapes", FruitColor="Green"},
new Fruit {FruitName="Tomato", FruitColor="Red" }
};
public TestingRealTimeUIUpdate()
{
this.InitializeComponent();
}
private void btnReload_Click(object sender, RoutedEventArgs e)
{
List<Comp> lstFruitBasket = new List<Comp>();
foreach( var item in FruitForFruitBasket)
{
FruitBasket = new Comp();
FruitBasket.Fruits = item;
FruitBasket.Presentations = PresentationForFruitBasket.ElementAt(2);
lstFruitBasket.Add(FruitBasket);
}
lstFruits.ItemsSource = lstFruitBasket;
}
private void btnChangeColor_Click(object sender, RoutedEventArgs e)
{
Random rnd = new Random();
FruitBasket.Presentations = PresentationForFruitBasket.ElementAt(rnd.Next(0, 3));
}
}
public class Comp : BindableBase
{
Fruit fruits = new Fruit();
Presentation presentations = new Presentation();
public Fruit Fruits
{
get { return fruits; }
set { SetProperty(ref fruits, value); }
}
public Presentation Presentations
{
get { return presentations; }
set { SetProperty(ref presentations, value); }
}
}
public class Fruit:BindableBase
{
private string fruitname;
private string fruitcolor;
public string FruitName
{
get { return fruitname; }
set { SetProperty(ref fruitname, value); }
}
public string FruitColor
{
get { return fruitcolor; }
set { SetProperty(ref fruitcolor, value); }
}
}
public class Presentation : BindableBase
{
private SolidColorBrush forecolor;
private string fontname;
public SolidColorBrush ForeColor
{
get { return forecolor; }
set { SetProperty(ref forecolor, value); }
}
public string FontName
{
get { return fontname; }
set { SetProperty(ref fontname, value); }
}
}
}
The BindableBase.cs
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Core;
namespace IQ.Main.ViewModels
{
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private CoreDispatcher dispatcher;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (Object.Equals(storage, value))
{
return false;
}
storage = value;
NotifyPropertyChanged(propertyName);
return true;
}
internal virtual async void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
try
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}
catch (Exception ex) { Debug.WriteLine(ex.Message); }
}
}
}
}
You can do something like this:
<ListBox Foreground="{x:Bind Foreground}" />
This works if the Foreground property is in your code-behind.
You can do something like this:
<ListBox Foreground="{Binding Foreground}" />
This works if the Foreground property is in your view-model.
You can also do this:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Foreground="{Binding Foreground}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This works if the Foreground property is in your model
Remember, you can always create a composite object. Like this:
class MyObject
{
public FruitObject Fruit { get; set; }
public PresentationObject Presentation { get; set; }
}
This will let you pass in multiple objects to any ItemControl.
Make sense? Best of luck.
I have code below inside Grid:
<Grid.RenderTransform>
<TranslateTransform
X="{Binding X, Converter={StaticResource HorizontalPositionConverter}}"
Y="{Binding Y, Converter={StaticResource VerticalPositionConverter}}"
/>
</Grid.RenderTransform>
How can I get binding of TranslateTransform.X or TranslateTransform.Y in code behind? I found this question but solution works for non-nested dependency properties. What to do when they are? I cannot consider binding to entire RenderTransform. I am developing winrt app, so multibinding is out of the game.
Here is the code behind binding. I didn't use a converter because my X and Y are defined double. In order to make a correct binding you have to use dependecy property or another notification mechanism (like INotifyPropertyChanged implementation). Here is code behind binding solution (not MVVM). I've added the button to test the moving.
1. XAML code:
<Window x:Class="TransformBindingSoHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RenderTransform>
<TranslateTransform
X="{Binding ElementName=This, Path=X, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Y="{Binding ElementName=This, Path=Y, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</Grid.RenderTransform>
<Button Content="Click" Width="100" Height="100" Click="ButtonBase_OnClick"></Button>
</Grid>
2. Code behind :
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty XProperty = DependencyProperty.Register(
"X", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
public double X
{
get { return (double) GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty YProperty = DependencyProperty.Register(
"Y", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
private static double _position;
public double Y
{
get { return (double) GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
X = ++_position;
Y = _position;
}
}
Update 1:
Here is code-behind based solution, there is no binding in XAML:
3. Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty XProperty = DependencyProperty.Register(
"X", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
public double X
{
get { return (double) GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty YProperty = DependencyProperty.Register(
"Y", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
private static double _position;
public double Y
{
get { return (double) GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
X = ++_position;
Y = _position;
}
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var grid = sender as Grid;
if(grid == null) return;
var transform = grid.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = InitTransformBinding();
grid.RenderTransform = transform;
}
else
{
InitTransformBinding(transform);
}
}
private TranslateTransform InitTransformBinding(TranslateTransform t = null)
{
var transform = t ?? new TranslateTransform();
var xBinding = new Binding();
xBinding.Source = this;
xBinding.Path = new PropertyPath("X");
xBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
xBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(transform, TranslateTransform.XProperty, xBinding);
var yBinding = new Binding();
yBinding.Source = this;
yBinding.Path = new PropertyPath("Y");
yBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
yBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(transform, TranslateTransform.YProperty, yBinding);
return transform;
}
}
4. XAML code:
<Window x:Class="TransformBindingSoHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Loaded="FrameworkElement_OnLoaded">
<Button Content="Click" Width="100" Height="100" Click="ButtonBase_OnClick"></Button>
</Grid>
Update 2, here on each button click you will scale the grid.
5. Xaml code:
Window x:Class="TransformBindingSoHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:transformBindingSoHelpAttempt="clr-namespace:TransformBindingSoHelpAttempt"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Window.DataContext>
<transformBindingSoHelpAttempt:MainViewModel/>
</Window.DataContext>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView ItemsSource="{Binding Items}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type transformBindingSoHelpAttempt:ItemDataContext}">
<Grid>
<Grid.RenderTransform>
<ScaleTransform
ScaleX="{Binding X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScaleY="{Binding Y, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid.RenderTransform>
<Button Content="{Binding ButtonContent}" Command="{Binding ButtonCommand}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
6. View models:
public class MainViewModel:BaseObservableObject
{
public MainViewModel()
{
Items = new ObservableCollection<ItemDataContext>(new List<ItemDataContext>
{
new ItemDataContext{ButtonContent = "A", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "B", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "C", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "D", X = 1.0, Y = 1.0},
});
}
public ObservableCollection<ItemDataContext> Items { get; set; }
}
public class ItemDataContext:BaseObservableObject
{
private ICommand _buttonCommand;
private object _buttonContent;
private double _x;
private double _y;
public double X
{
get { return _x; }
set
{
_x = value;
OnPropertyChanged();
}
}
public double Y
{
get { return _y; }
set
{
_y = value;
OnPropertyChanged();
}
}
public ICommand ButtonCommand
{
get { return _buttonCommand ?? (_buttonCommand = new DelegateCommand(Target)); }
}
public object ButtonContent
{
get { return _buttonContent; }
set
{
_buttonContent = value;
OnPropertyChanged();
}
}
private void Target(object obj)
{
X += 0.2;
Y += 0.2;
}
}
7. How it is looks like:
Please keep in mind that the last update solution is based on LayouTransform and re-build the view on each button click (makes it to be scaled).
Regards,
I have tried all morning to get this to work with no luck. I am using DynamicDataDisplay (D3) to display a graph. Here is my simple view defined using xaml.
<Window x:Class="BMSVM_Simulator.View.GraphWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:BMSVM_Simulator.ViewModel"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
x:Name="ThisGraphWindowInstance"
Title="Plot" Height="500" Width="750"
WindowStartupLocation="CenterOwner"
Icon="../res/qualcomm_q_icon.ico.ico"
MinWidth="400" MinHeight="300">
<Window.DataContext>
<ViewModel:GraphWindowPresenter/>
</Window.DataContext>
<Grid>
<d3:ChartPlotter Name="plotter" Margin="10,10,20,10">
<d3:InjectedPlotter Name="innerPlotter" Background="Aqua" SetViewportBinding="False">
<d3:VerticalAxis Placement="Right"/>
<d3:VerticalAxisTitle Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}" Placement="Right"/>
</d3:InjectedPlotter>
<d3:Header FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}"/>
<d3:HorizontalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
</d3:ChartPlotter>
</Grid>
</Window>
The issue is that the:
<d3:VerticalAxisTitle Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}" Placement="Right"/>
in the InjectedPlotter does not display at all when I use the current setup with the Content bound to Path=yAxis2. I set at breakpoint and I see that yAxis2 is actually a defined string and that it is not null.
When I actually hardcode a value such that Content="DEFAULT TITLE", so it then becomes :
<d3:VerticalAxisTitle Content="DEFAULT TITLE" Placement="Right"/>
the title displays fine.
Does anyone know why this is happening?
Here is the code behind for reference:
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView));
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
public void ShowGraph()
{
// consume ChartData
this.yAxis1 = ChartData.yAxisTitle1;
this.yAxis2 = "AXIS 2 TITLE..SHOW UP!";
.....
}
EDIT >>>>>>>>>
using BMSVM_Simulator.ViewModel;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Microsoft.Research.DynamicDataDisplay.Navigation;
using Microsoft.Research.DynamicDataDisplay.PointMarkers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BMSVM_Simulator.View
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class GraphWindowView : Window
{
#region Fields
private readonly int DEFAULT_AXIS_WIDTH = 20;
private readonly Pen[] colors = {
new Pen(Brushes.Blue, 2),
new Pen(Brushes.DarkGreen, 2),
new Pen(Brushes.DarkMagenta, 2),
new Pen(Brushes.DarkSalmon, 2),
new Pen(Brushes.Maroon, 2),
new Pen(Brushes.Orange, 2),
new Pen(Brushes.SkyBlue, 2)
};
#endregion
#region DependencyProperties
public static readonly DependencyProperty yAxis1Property =
DependencyProperty.Register("yAxis1", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty titleProperty =
DependencyProperty.Register("title", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty xAxisProperty =
DependencyProperty.Register("xAxis", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty DatesProperty =
DependencyProperty.Register("Dates", typeof(EnumerableDataSource<int>), typeof(GraphWindowView));
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(EnumerableDataSource<int>), typeof(GraphWindowView));
public static readonly DependencyProperty ChartDataProperty =
DependencyProperty.Register("ChartData", typeof(ChartData), typeof(GraphWindowView));
public static readonly DependencyProperty rightAxisWidthProperty =
DependencyProperty.Register("rightAxisWidth", typeof(int), typeof(GraphWindowView));
public int rightAxisWidth
{
get { return (int)GetValue(rightAxisWidthProperty); }
set { SetValue(rightAxisWidthProperty, value); }
}
public ChartData ChartData
{
get { return (ChartData)GetValue(ChartDataProperty); }
set { SetValue(ChartDataProperty, value); }
}
public EnumerableDataSource<int> Dates
{
get { return (EnumerableDataSource<int>)GetValue(DatesProperty); }
set { SetValue(DatesProperty, value); }
}
public EnumerableDataSource<int> Data
{
get { return (EnumerableDataSource<int>)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public string xAxis
{
get { return (string)GetValue(xAxisProperty); }
set { SetValue(xAxisProperty, value); }
}
public string yAxis1
{
get { return (string)GetValue(yAxis1Property); }
set { SetValue(yAxis1Property, value); }
}
public string title
{
get { return (string)GetValue(titleProperty); }
set { SetValue(titleProperty, value); }
}
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
#endregion
public GraphWindowView()
{
InitializeComponent();
rightAxisWidth = DEFAULT_AXIS_WIDTH;
}
public void ShowGraph()
{
// consume ChartData
this.xAxis = ChartData.xAxisTitle;
this.yAxis1 = ChartData.yAxisTitle1;
this.yAxis2 = "AXIS 2 TITLE..SHOW UP!"; // ChartData.yAxisTitle2;
this.title = ChartData.title;
this.rightAxisWidth = DEFAULT_AXIS_WIDTH;
// list of data points
List<DataSet> dataSets = this.ChartData.dataPoints;
int colorCounter = 0;
int rightAxisCount = 0;
foreach (DataSet set in dataSets)
{
set.dates.SetXMapping(x => x);
set.data.SetYMapping(x => x);
CompositeDataSource compositeDataSource1 = new
CompositeDataSource(set.dates, set.data);
if (set.axis == AxisSide.LEFT)
{
plotter.AddLineGraph(compositeDataSource1, colors[colorCounter % colors.Length],
new CirclePointMarker { Size = 8.00, Fill = Brushes.Red },
new PenDescription(set.legendTitle));
}
else
{
innerPlotter.AddLineGraph(compositeDataSource1, colors[colorCounter % colors.Length],
new CirclePointMarker { Size = 8.00, Fill = Brushes.Red },
new PenDescription(set.legendTitle));
rightAxisCount++;
}
colorCounter++;
}
// if there is nothing plotted against the right axis, don't show it
if (rightAxisCount == 0)
{
rightAxisWidth = 0;
}
plotter.Viewport.FitToView();
// there are duplicate legends, so we hide one
plotter.LegendVisibility = Visibility.Hidden;
Show();
}
}
}
In the code you provided, you have set the datacontext as an object of GraphWindowPresenter, but while declaring the dependancy property you have set the GraphWindowView object. Please make sure that you set the appropriate object as datacontext
<Window.DataContext>
< ViewModel:GraphWindowPresenter/>
< /Window.DataContext>
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView))
Try :
<d3:VerticalAxisTitle Content="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=yAxis2,Mode=OneWay}" />
I ran a quick test binding a TextBox.Text, and the code you have posted works.
<Window x:Class="WpfApplication2.MainWindow"
x:Name="TestWindow" ...>
<StackPanel>
<!-- both bindings work -->
<TextBlock Text="{Binding ElementName=TestWindow, Path=yAxis2}" />
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}, Path=yAxis2}" />
</StackPanel>
</Window>
public partial class MainWindow : Window
{
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(MainWindow));
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
public MainWindow()
{
InitializeComponent();
this.yAxis2 = "TESTING";
}
}
So my best guess is that either
you are not calling ShowGraph() on the window
or the VerticalAxisTitle object is not one that exists in the Visual Tree, much like some other WPF objects like DataGridColumn
To determine if the first issue is your problem, simply ensure you are calling ShowGraph() in the constructor behind your window, or just set yAxis2 the way I have here for testing.
You could also use a tool like Snoop that is very useful for debugging runtime databindings.
If that is done and it's still not showing up correctly, then you may need to do some more research into the VerticalAxisTitle to find workarounds on how to bind it correctly. If you have trouble finding anything specific to VerticalAxisTitle, try looking up how it's done for DataGridColumn, such as this answer.
(As a side note, it's a standard convention to capitalize public properties, so your property should be YAxis2. Just my OCD kicking in.) :)