Binding User Control Property with another Property - c#

In my Windows Store Application I want to bind a Property in a user control with another Property in logical class
the User Control "Card_UC.xaml.cs" contains this Property:
public string Card_ID
{
get { return (string)GetValue(Card_ID_Property); }
set { SetValue(Card_ID_Property, value); }
}
public DependencyProperty Card_ID_Property =
DependencyProperty.Register(
"Card_ID",
typeof(string),
typeof(Card_UC),
new PropertyMetadata(null));
and in my logical class "Card_Data.cs":
public string Card_ID { get; set; }
In Main Page I want to make a Grid of this Cards using data binding like this
<GridView
x:Name="UI_GView_Cards"
ItemsSource="{Binding}">
<GridView.ItemTemplate>
<DataTemplate>
<local:CardControl
x:Name="UC_Card"
CardPressed="CardControl_CardPressed"
ID="{Binding Path=Card_ID, ElementName=Card_UC, Mode=TwoWay}"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
all the other Properties binding in the "Card_UC.xaml" working except Card_ID
the problem now is that the application crashes every time I access the ID Property using
return (string)GetValue(Card_ID_Property);
Error: "Object reference not set to an instance of an object."
Problem Fixed:
the problem in this line:
ID="{Binding Path=Card_ID, ElementName=Card_UC, Mode=TwoWay}"
changed to:
ID="{Binding Card_ID}"
Edit:
Fixed "Copy/Paste" mistake.
Re-Format the question.

It looks like you have a cut and paste error:
public string Card_ID
{
get { return (string)GetValue(UCAppsProperty); }
set { SetValue(UCAppsProperty, value); }
}
to:
public string Card_ID
{
get { return (string)GetValue(Card_ID_Property); }
set { SetValue(Card_ID_Property, value); }
}

Related

Passing ModelData (context) between UserControls (views) MVVM Prism

I'm woking on a project and I have three ViewModels: ObjectDetailsViewMode has a Context (property linking to a model) of type ObjectBase; PropertyTextViewModel has a Context of type PropertyText and PropertyNumberViewModel has a Context of type PropertyNumber.
Below is the structure of the Models:
public class ObjectBase : ModelBase
{
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
public DataCollection<PropertyBase> Properties { get; } = new DataCollection<PropertyBase>();
}
public class PropertyText : PropertyBase
{
private string _default;
public string Default
{
get { return _default; }
set { SetProperty(ref _default, value); }
}
}
public class PropertyNumber : PropertyBase
{
private double _default = 0;
public double Default
{
get { return _default; }
set { SetProperty(ref _default, value); }
}
private double _minValue = 0;
public double MinValue
{
get { return _minValue; }
set { SetProperty(ref _minValue, value); }
}
private double _maxValue = 0;
public double MaxValue
{
get { return _maxValue; }
set { SetProperty(ref _maxValue, value); }
}
}
Regarding the views I have one for each ViewModel. The ObjectDetailsView is a use control that has a TextBox for editing the Object.Name, two buttons to add new PropertyText/PropertyNumber to the Object.Properties and an ItemsControl connected to that Object.Properties.
Each PropertyBase in the ItemsControl (ItemsSource) is resolved into a new view using the DataTemplate marker:
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView />
</DataTemplate>
<DataTemplate DataType="{x:Type models:PropertyNumber}">
<views:PropertyNumberView />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
As I'm using PRISM the correct ViewModel is automatically created for me and the view DataContext is then set to the new ViewModel. My problem is I need to pass the new Property from the Object.Properties list to the newly created View's ViewModel and store it in the Context property I have there.
I can't avoid creating a View/ViewModel for each property type because there is some under-the-hood logic on some Property types (not the ones I described here.. but I have other types like Boolean, Reference, Enum...)
So I really need to pass a value to the ViewModel I tried to use
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView Context="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type models:PropertyNumber}">
<views:PropertyNumberView Context="{Binding}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Be aware that Context is a custom property I created inside the ViewModel's to store the ModelContext. I even created a DependencyProperty in the View's behind code:
public PropertyBase Context
{
get { return (PropertyBase)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContextProperty =
DependencyProperty.Register("Context", typeof(PropertyBase), typeof(PropertyTextView), new PropertyMetadata(null));
But it doesn't get linked to the ViewModels set event (I made a break point there and... nothing). I even tried a SetBinding in the PropertyTextView code-behind (constructor):
string propertyInViewModel = "Context";
var bindingViewMode = new Binding(propertyInViewModel) { Mode = BindingMode.TwoWay };
this.SetBinding(ContextProperty, bindingViewMode);
No luck with any of these... I' really stuck.
Something More Simple
If the PropertyTextView has this dependency property.
public string Context
{
get { return (PropertyBase)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
// Using a DependencyProperty as the backing store for Context. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContextProperty =
DependencyProperty.Register("Context", typeof(string), typeof(PropertyTextBuilderView), new PropertyMetadata(null));
I should be able to do:
right?! Why isn't the public property "Context" not being called (I placed a breakpoint there and I get nothing).
Instead of just setting the Context Property of your View to a new Binding you need to assign the Current DataContext like so:
<views:PropertyNumberView Context="{Binding .}"/>
This should assign the Current Views.DataContext Property to your new View.
If you're in an DataTemplate you probably need to specify the RelativeSource:
<views:PropertyNumberView Context="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView Context="{Binding .}"/>
</DataTemplate>
<ItemsControl.Resources>
</ItemsControl>
As I'm using PRISM the correct ViewModel is automatically created for me
You don't have to use view-first with Prism. The ViewModelLocator is there to help, if you chose to, but view model-first is possible, too.
If I understand you correctly, you have a view model and want to populate a list with child view models. So do just that:
internal class ParentViewModel : BindableBase
{
public ParentViewModel( ParentModel parentModel, IChildViewModelFactory factory )
{
Children = new object[] { factory.CreateTextViewModel(parentModel.TextProperty), factory.CreateNumberViewModel(parentModel.NumberProperty) };
}
public IEnumerable Children { get; }
}
and map the different child view models to child views via DataTemplates.
parentModel.WhateverProperty will have a Name and Value properties as well as setter for the value, probably...

WPF ComboBox validation error when binding to IEnumerable of Enum values

For the purpose of code reuse, I am attempting to bind a ComboBox ItemsSource to an enumerable of enum values defined in a viewmodel. (I am aware of the strategies for binding directly to the enum, but in order to achieve code reuse I need to bind to an enumerable.) On viewmodel construction, I set the selected item to the first value of the enumerable. When the UI first launches, however, the combobox loads with validation error:
Value '' could not be converted.
This error does not occur when I use the same XAML to bind to an enumerable of classes. After I select an enum value, I get no more validation errors and the UI works as intended. How do I avoid this error and get the combobox to display the selected item on startup?
The code details... I have a service implementing IAcquire<T> which returns an enumerable of enum values:
public interface IAcquire<T>
{
IReactiveList<T> Items { get; }
}
My viewmodel inheritance looks something like this:
class GranularitySelectionViewModel : ChartFilterSelectionBase<DataGranularity>
{
public GranularitySelectionViewModel([NotNull] IAcquire<DataGranularity> service)
: base(service, "Granularity")
{}
}
class ChartFilterSelectionBase<T> : SelectionViewModelBase
{
private readonly IAcquire<T> _service;
internal ChartFilterSelectionBase([NotNull] IAcquire<T> service, string label)
:base(label)
{
foreach (var value in service.Items)
{
Items.Add(value);
}
SelectedItem = Items.FirstOrDefault();
}
private readonly IReactiveList<T> _items = new ReactiveList<T>();
public new IReactiveList<T> Items
{
get { return _items; }
}
private T _selectedItem;
public new T SelectedItem
{
get { return _selectedItem; }
set { SetProperty(ref _selectedItem, value); }
}
}
public class SelectionBaseViewModel
{
protected SelectionBaseViewModel([NotNull] string label )
{
if (label == null) throw new ArgumentNullException("label");
_label = label;
}
private readonly string _label;
public string Label
{
get { return _label; }
}
//Placeholder to be overridden in derived class.
public object SelectedItem { get; set; }
//Placeholder to be overridden in derived class.
public IReactiveList<object> Items { get; private set; }
}
The XAML is as follows:
<DataTemplate DataType="{x:Type viewModels:SelectionBaseViewModel}">
<StackPanel Orientation="Vertical">
<Label Content="{Binding Label}" ContentStringFormat="{}{0}:" Margin="5,5,5,0"/>
<ComboBox Margin="5,0,5,5" ItemsSource="{Binding Items, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderThickness="1" BorderBrush="White">
</ComboBox>
</StackPanel>
</DataTemplate>

WPF Binding to Custom Property Not Working

I have this control to display a list of usercontrols
<ItemsControl x:Name="LayersList" Margin="10,284,124,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<NaturalGroundingPlayer:LayerControl Item="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The LayerControl control contains this code
public partial class LayerControl : UserControl {
public LayerItem Item { get; set; }
public static readonly DependencyProperty ItemProperty =
DependencyProperty.Register(
"Item",
typeof(LayerItem),
typeof(LayerControl),
new PropertyMetadata(null));
public LayerControl() {
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e) {
// This doesn't work because Item remains null
MainWindow.Instance.LayersList.Items.Remove(Item);
}
}
LayerItem contains this
[PropertyChanged.ImplementPropertyChanged]
public class LayerItem {
public LayerType Type { get; set; }
public string FileName { get; set; }
}
public enum LayerType {
Audio,
Video,
Image
}
Problem is: The Binding is setting the Item property to null. If I change the binding to {Binding Type} instead of {Binding} (and adapt the property type accordingly), then it works. But I can't find a way to bind the whole object. What am I doing wrong?
On a side note, I tried setting ItemsControl.ItemsSource to a ObservableCollection<LayerItem> but that didn't seem to work. Adding items directly to ItemsControl.Items is working. Any idea why that is?
You have incorrectly implemented a dependency property. You should use GetValue and SetValue methods instead of creating an auto-property.
public static readonly DependencyProperty ItemProperty =
DependencyProperty.Register(
"Item", typeof(LayerItem), typeof(LayerControl));
public LayerItem Item
{
get { return (LayerItem)GetValue(ItemProperty); }
set { SetValue(ItemProperty, value); }
}
P.S. You shouldn't access controls like this: MainWindow.Instance.LayersList.Items.Remove(Item). You should use MVVM instead. I'm also not convinced this property is required at all. DataContext may be enough.

Binding a property of an object of a class inside another doesn't work

I have an strange problem with binding in WPF.
There is a simple sample of what i'm doing:
public class Project
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title= value;
RaisePropertyChanged("Title");
}
}
}
public class People
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name= value
RaisePropertyChanged("Name");
}
}
private Project _project;
public Project Project
{
get { return _project; }
set
{
_project= value;
RaisePropertyChanged("Project");
}
}
}
Now I bound a grid to an instance of People in the view and it can bind controls to Project and Name of People class, but I really can't understand why I can not bind to Project.Title.
I write my XAML code like this:
<TextBox Text="{Binding Name}"/>
<Combobox .... SelectedItem="{Binding Project}"/>
<TextBox Text="{Binding Project.Title}"/>
The first two controls above get bounded correctly but the last TextBox doesn't. I have no idea why it can access to Project but not Project.Title? It's an another weird thing I've already seen in WPF!
Perhaps your combo box selection is not setting the selected value without using Mode=TwoWay:
<Combobox .... SelectedItem="{Binding Project, Mode=TwoWay}"/>
Once the Project is set, the property is set, the Title will show.
Try "Path=Project.Title".
Worked for me in a same case.
if your DataContext is an instance of your Person object it should work. you can check your bindings at runtime with Snoop (http://snoopwpf.codeplex.com/) btw. give it a try :)
you can also do
<TextBox DataContext="{Binding Project, Mode=OneWay}" Text="{Binding Title}"/>
Thank you everyone,but the problem wasn't what i thought.Actually i used PropertyChanged.Fody to inject INotifyPropertyChanged into properties,but it seems it doesn't do what i expected.I implemented INotifyPropertyChanged myself and it works fine now.

Silverlight: binding on complex property

First of all i will try to explain what i am doing. I am trying to draw a chess board. I have a user controll for cell
<Grid x:Name="LayoutRoot">
<Border BorderThickness="0" Margin="0" Background="{Binding CellColor, ElementName=userControl, Mode=TwoWay}"/>
<Border x:Name="ValidMoveMarker" BorderThickness="0" Margin="0" Background="#FFC1CAB4" Opacity="0"/>
<Image x:Name="img" Source="{Binding source, ElementName=userControl, Mode=TwoWay}" Cursor="Hand"/>
In code behind of this CellControl i have 2 dpProperties
public eColor? PieceColor
{
get { return (eColor?)GetValue(PieceColorProperty); }
set { SetValue(PieceColorProperty, value);}
}
public static readonly DependencyProperty PieceColorProperty = DependencyProperty.Register("PieceColor", typeof(eColor?), typeof(CellControl), null);
public eType? PieceType
{
get { return (eType?)GetValue(PieceTypeProperty); }
set { SetValue(PieceTypeProperty, value);}
}
public static readonly DependencyProperty PieceTypeProperty = DependencyProperty.Register("PieceType", typeof(eType?), typeof(CellControl), null);
where eColor and eType are enumerators. Here I also have one property
public ImageSource source
{
get
{
if (PieceColor == eColor.White)
{
switch (PieceType)
{
case eType.Pawn:
return new BitmapImage(new Uri("/PO.PC;component/Images/chess_piece_white_pawn_T.png", UriKind.Relative));
case eType.Knight:
return new BitmapImage(new Uri("/PO.PC;component/Images/chess_piece_white_knight_T.png", UriKind.Relative));
...
default:
return null;
}
}
else
{
switch (PieceType)
{
case eType.Pawn:
}
}
}
Now problem is when i try to use the control like this
<PP_Controls:CellControl PieceType="{Binding type, Mode=TwoWay}" PieceColor="{Binding color, Mode=TwoWay}"
where
private eColor? _color;
public eColor? color
{
get { return _color; }
set
{
_color = value;
OnPropertyChanged("color");
}
}
private eType? _type;
public eType? type
{
get { return _type; }
set
{
_type = value;
OnPropertyChanged("type");
}
}
nothings happens. But if i use control like this
<PP_Controls:CellControl PieceType="Bishop" PieceColor="Black"
it is working perfectly. Am I missing something in my bindings? Is this because "source" property is not dependency property itself? How can I workaround my problem?
Your target properties are dependency properties, and the source properties are CLR properties implementing INotifyPropertyChanged, so your binding {Binding type} etc should work - assuming that the DataContext to your "use with binding" is the type with the color/type properties. You should be able to tell if these bindings are failing by looking at the Output window when you run your application under the debugger (in Silverlight 5 you could also use a binding breakpoint, or otherwise you can apply a trivial ValueConverter to the binding to set a breakpoint for debugging).
However your control's source property depends on two other properties in a 'lazy' fashion. You are binding to the source property, but nothing will cause this binding to update when the property's computed value changes. You should add a dependency property changed handler to PieceColor and PieceType which calls OnPropertyChanged("source") (or equivalently convert it to a DP or notifying property, and explicitly recompute the value).

Categories