TwoWay Binding to a UserControl Property - WinUI 3 - xaml

I have the following UserControl that is meant to be a pre-formatted TextBox with a label:
XAML:
<UserControl
x:Class="Aerloc.Components.LabeledTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Aerloc.Components"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Text="{x:Bind Caption, Mode=TwoWay}" Style="{StaticResource LabelTextBox}" Grid.Column="0"/>
<TextBox Text="{x:Bind Value, Mode=TwoWay}" IsEnabled="{x:Bind IsEditable, Mode=OneWay}" Style="{StaticResource FieldTextBox}" Grid.Column=1/>
</Grid>
</UserControl>
Code Behind:
public sealed partial class LabeledTextBox : UserControl
{
public DependencyProperty CaptionProperty = DependencyProperty.Register(nameof(Value), typeof(string), typeof(LabeledTextBox), new PropertyMetadata(null));
public DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Caption), typeof(string), typeof(LabeledTextBox), new PropertyMetadata(null));
public DependencyProperty IsEditableProperty = DependencyProperty.Register(nameof(IsEditable), typeof(bool), typeof(LabeledTextBox), new PropertyMetadata(null));
public string Caption
{
get => (string)GetValue(CaptionProperty);
set => SetValue(CaptionProperty, value);
}
public string Value
{
get => (string)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public bool IsEditable
{
get => (bool)GetValue(IsEditableProperty);
set => SetValue(IsEditableProperty, value);
}
public LabeledTextBox()
{
this.InitializeComponent();
}
}
I am having trouble with TwoWay Binding the Value passed into the UserControl. When I create the binding (code below) I get an error message saying "TwoWay binding target 'Value' must be a dependency property". How can I two way bind the Value in my UserControl to the ViewModel?
<components:LabeledTextBox
Caption="First Name:"
Value="{x:Bind ViewModel.SelectedUser.FirstName, Mode=TwoWay}"
IsEditable="{x:Bind ViewModel.IsAddEditModeEnabled}"/>

DependencyProperty needs to be static readonly. You also have your DependencyProperties' name wrong.
Fix this,
public DependencyProperty CaptionProperty =
DependencyProperty.Register(
nameof(Value),
typeof(string),
typeof(LabeledTextBox),
new PropertyMetadata(null));
public DependencyProperty ValueProperty =
DependencyProperty.Register(
nameof(Caption),
typeof(string),
typeof(LabeledTextBox),
new PropertyMetadata(null));
to this,
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register(
nameof(Caption),
typeof(string),
typeof(LabeledTextBox),
new PropertyMetadata(null));
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
nameof(Value),
typeof(string),
typeof(LabeledTextBox),
new PropertyMetadata(null));

Related

How do I set ItemDisplayBinding for a Picker inside a Content View

I have a ContentPage, which has a reference to a ContentView. Inside this content view I have a custom Picker control, I have binded other properties e.g. ItemsSource, ItemSelected but ItemDisplayBinding is being a pain. I can see in the code for the Xamarin Forms Picker class that it's not a straightforward bindable property field like the rest. At the moment I am getting this error when I click on the Picker. I know it that specific display binding property because the picker works fine without me initialising it.
Error
Java.Lang.NullPointerException
Message=Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
Content Page
<controls:MyPickerView PickerTitle="Select time period"
PickerItemDisplayBinding="{Binding Display}"
PickerItemsSource="{Binding AppointmentRangeOptions}"/>
MyPickerView.xaml
<ContentView.Content>
<Frame >
<Picker Title = "{Binding PickerTitle}"
HorizontalOptions="FillAndExpand"
IsEnabled="{Binding PickerIsEnabled}"
ItemDisplayBinding="{Binding PickerItemDisplayBinding}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
ItemsSource="{Binding PickerItemsSource, Mode=OneWay}"/>
</Frame>
</ContentView.Content>
MyPickerView.xaml.cs
public partial class MyPickerView : ContentView
{
public MyPickerView()
{
InitializeComponent();
Content.BindingContext = this;
}
public static readonly BindableProperty PickerIsEnabledProperty = BindableProperty.Create(
nameof(PickerIsEnabled),
typeof(bool),
typeof(MyPickerView),
null);
public bool PickerIsEnabled
{
get => (bool)GetValue(PickerIsEnabledProperty);
set => SetValue(PickerIsEnabledProperty, value);
}
public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(
nameof(SelectedItem),
typeof(object),
typeof(MyPickerView),
null);
public object SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
public static readonly BindableProperty PickerTitleProperty = BindableProperty.Create(
nameof(PickerTitle),
typeof(string),
typeof(MyPickerView),
null);
public string PickerTitle
{
get => (string)GetValue(PickerTitleProperty);
set => SetValue(PickerTitleProperty, value);
}
public static readonly BindableProperty PickerItemDisplayBindingProperty = BindableProperty.Create(
nameof(PickerItemDisplayBinding),
typeof(string),
typeof(MyPicker)
);
public string PickerItemDisplayBinding
{
get => (string)GetValue(PickerItemDisplayBindingProperty);
set => SetValue(PickerItemDisplayBindingProperty, value);
}
public static readonly BindableProperty PickerItemsSourceProperty = BindableProperty.Create(
nameof(PickerItemsSource),
typeof(IList),
typeof(MyPickerView));
public IList PickerItemsSource
{
get => (IList)GetValue(PickerItemsSourceProperty);
set => SetValue(PickerItemsSourceProperty, value);
}
}
Indeed, this property is a pain.
I solved it with a little trick:
MyPickerView.xaml.cs
public BindingBase ItemDisplayBinding
{
get => MyPicker?.ItemDisplayBinding;
set => MyPicker.ItemDisplayBinding = value;
} // no need to define BindableProperty
MyPickerView.xaml
<ContentView.Content>
<Frame>
<Picker x:Name="MyPicker" />
</Frame>
</ContentView.Content>
Content Page
<controls:MyPickerView
PickerTitle="Select time period"
ItemDisplayBinding="{Binding Display}"
PickerItemsSource="{Binding AppointmentRangeOptions}"/>

Need to create a FeetInches usercontrol in XAML

I am trying to create a UWP feet/inches control that takes a value in inches, and splits it into two fields, feet and inches. The control, when updated by the user, should update the backend viewmodel with the new inches value.
Requirements
Change the value in 1, then the values in 2 and 3 update
accordingly.
Change the value in 3, the values in 1 and 2 update accordingly
The way I have this written it gets into an endless loop. I am not even sure the FeetInches control is written properly. How would I change this to meet the above requirements?
Downloadable/runnable code is available in GitHub, but here it is inline per SO guidelines...
App.xaml
<Application x:Class="UWPFeetInches.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWPFeetInches">
<Application.Resources>
<ResourceDictionary>
<local:NullDecimalConverter x:Key="NullDecimalConverter" />
</ResourceDictionary>
</Application.Resources>
</Application>
MainPage.xaml
<Page
x:Class="UWPFeetInches.MainPage"
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="using:UWPFeetInches"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
Text="Enter Inches Value:"
Margin="0,50,10,0"
VerticalAlignment="Center" />
<TextBox Grid.Row="0" Grid.Column="1"
Margin="0,50,10,0"
Text="{x:Bind ViewModel.Inches, Mode=TwoWay, Converter={StaticResource NullDecimalConverter}}" />
<TextBlock Grid.Row="1" Grid.Column="0"
Margin="0,50,10,0"
Text="Inches Display:"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Margin="0,50,10,0"
Text="{x:Bind ViewModel.InchesDisplay, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
Text="Feet / Inches Control:"
Margin="0,50,10,0"
VerticalAlignment="Center" />
<local:FeetInches Grid.Row="2" Grid.Column="1"
Margin="0,50,10,0"
Value="{x:Bind ViewModel.Inches, Mode=TwoWay}" />
</Grid>
</Page>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace UWPFeetInches
{
/// <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 MainPage()
{
this.InitializeComponent();
this.ViewModel = new InchesViewModel();
}
public InchesViewModel ViewModel { get; set; }
}
public class InchesViewModel : INotifyPropertyChanged
{
private decimal? _Inches;
public decimal? Inches
{
get { return _Inches; }
set
{
if (value != _Inches)
{
_Inches = value;
InchesDisplay = _Inches == null ? "{null}" : _Inches.ToString();
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Inches)));
}
}
}
private string _InchesDisplay;
public string InchesDisplay
{
get { return _InchesDisplay; }
set
{
if (value != _InchesDisplay)
{
_InchesDisplay = value;
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(InchesDisplay)));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
NullDecimalConverter.cs
using System;
using Windows.UI.Xaml.Data;
namespace UWPFeetInches
{
public sealed class NullDecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is decimal m)
{
return m == 0 ? "" : m.ToString();
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (!string.IsNullOrWhiteSpace(value?.ToString()))
{
if (Decimal.TryParse(value.ToString(), out decimal m))
{
return m;
}
}
return null;
}
}
}
FeetInches.xaml
<UserControl
x:Class="UWPFeetInches.FeetInches"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:UWPFeetInches"
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">
<StackPanel Orientation="Horizontal">
<TextBox Header="Feet"
Margin="0,0,10,0"
Text="{x:Bind Feet, Mode=TwoWay}" />
<TextBox Header="Inches"
Text="{x:Bind Inches, Mode=TwoWay, Converter={StaticResource NullDecimalConverter}}" />
</StackPanel>
</UserControl>
FeetInches.xaml.cs
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace UWPFeetInches
{
public sealed partial class FeetInches : UserControl
{
public FeetInches()
{
this.InitializeComponent();
}
#region Feet
public int Feet
{
get { return (int)GetValue(FeetProperty); }
set
{
SetValue(FeetProperty, value);
}
}
public static readonly DependencyProperty FeetProperty = DependencyProperty.Register(nameof(Feet), typeof(int), typeof(FeetInches), new PropertyMetadata(0, OnPropertyChanged));
#endregion
#region Inches
public decimal Inches
{
get { return (decimal)GetValue(InchesProperty); }
set
{
SetValue(InchesProperty, value);
}
}
public static readonly DependencyProperty InchesProperty = DependencyProperty.Register(nameof(Inches), typeof(decimal), typeof(FeetInches), new PropertyMetadata(0M, OnPropertyChanged));
#endregion
#region Value
public decimal? Value
{
get { return (decimal)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(decimal?), typeof(FeetInches), new PropertyMetadata(null, ValueOnPropertyChanged));
#endregion
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as FeetInches;
control.Value = control.Feet * 12 + control.Inches;
}
private static void ValueOnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as FeetInches;
var inches = control.Value;
control.Feet = inches.HasValue ? (int)(inches.Value / 12M) : 0;
control.Inches = inches.HasValue ? inches.Value - (control.Feet * 12M) : 0M;
}
}
}
When you subscribe the PropertyChanged event for the three dependency properties, it will get into an endless loop. You could try to subscribe the LostFocus event of TextBox in FeetInches.xaml to replace your OnPropertyChanged event for your Feet and Inches properties, in that case, it won't get into an endless loop. For example:
.xaml:
<TextBox Header="Feet"
Margin="0,0,10,0"
x:Name="MyFeet"
Text="{x:Bind Feet, Mode=TwoWay}" LostFocus="TextBox_LostFocus"/>
<TextBox Header="Inches"
x:Name="MyInches"
Text="{x:Bind Inches, Mode=TwoWay, Converter={StaticResource NullDecimalConverter}}" LostFocus="TextBox_LostFocus"/>
.cs:
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var textbox = sender as TextBox;
Decimal.TryParse(textbox.Text, out decimal m);
if (textbox.Name.Equals("MyFeet"))
{
this.Value = m * 12 + this.Inches;
}
else
{
this.Value = this.Feet * 12 + m;
}
}
When triggered the LostFocus event, the property bound with the Text of TextBox has not changed, so you need to use the value of Text directly instead of the Feet/Inches property and you need to judge which TextBox triggers this event.
Or if you still want to use OnPropertyChanged event for Feet and Inches properties instead of LostFocus event, you can declare a property(e.g. bool valueChanged) to limit the calling of ValueOnPropertyChanged and OnPropertyChanged event.
.cs:
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (valueChanged == false) {
var control = d as FeetInches;
control.Value = control.Feet * 12 + control.Inches;
}
}
private static bool valueChanged = false;
private static void ValueOnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
valueChanged = true;
var control = d as FeetInches;
var inches = control.Value;
control.Feet = inches.HasValue ? (int)(inches.Value / 12M) : 0;
control.Inches = inches.HasValue ? inches.Value - (control.Feet * 12M) : 0M;
valueChanged = false;
}
Update:
You can use the Property property from DependencyPropertyChangedEventArgs data to check which dependency property changes.
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.Property == FeetProperty) {
//do something
}
else{
//do something
}
}

Binding value is not updating on entry control from other xaml

I have an entry control in my XAML where I set the initial value on page appear through data binding. Initially the value is appearing but when I am updating it from another view model it is not getting updated on UI.
Below is the XAML code and XAML.CS
<ListView
x:Name="workList"
Grid.Row="2"
SeparatorColor="{DynamicResource AccentColor}"
ItemsSource="{ Binding WorkItems }"
Margin="5"
CachingStrategy="RecycleElement"
RowHeight="440"
SeparatorVisibility="Default"
SelectionMode="None"
HasUnevenRows="False">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<local:LoadItemPutawayTemplate />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
mc:Ignorable="d"
x:Class="Sanipex.LoadItemPutawayTemplate">
<Grid
RowSpacing="0"
Padding="0"
Margin="0,10,0,0"
>
<Grid.RowDefinitions>
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<Entry
x:Name="OverrideLoc"
Grid.Row="0"
TextColor="Black"
WidthRequest="110"
Text="{Binding toLocation}"
grial:EntryProperties.BorderCornerRadius="10"
grial:EntryProperties.BorderStyle="RoundRect"
grial:EntryProperties.BorderColor="Black"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"
Focused="OverrideLoc_Focused"
TextChanged="OverrideLoc_TextChanged"
grial:EntryProperties.HorizontalPadding="5"
FontAttributes="Bold"
PlaceholderColor="Black"
FontSize="20"/>
</Grid>
public partial class ItemPutAway : ContentPage
{
private static ItemPutAwayViewModel obj;
public ItemPutAway()
{
InitializeComponent();
obj = new ItemPutAwayViewModel();
BindingContext = obj;
}
public static ItemPutAwayViewModel itemPutAwayViewModel
{
get
{
return obj;
}
}
protected override async void OnAppearing()
{
obj.LoadData();
}
}
Below is my first view model code
public class ItemPutAwayViewModel : INotifyPropertyChanged
{
private IList<WorkItem> workItems;
public event PropertyChangedEventHandler PropertyChanged;
public string ltoLocation;
public string toLocation
{
get => ltoLocation;
set
{
ltoLocation = value;
OnPropertyChanged(nameof(toLocation));
}
}
public IList<WorkItem> WorkItems
{
get => workItems;
set
{
workItems = value;
OnPropertyChanged(nameof(WorkItems));
}
}
public void LoadData()
{
WorkItems = App.dataManager.GetItemPutAwayWorks();
}
public void setLocation(string _location)
{
toLocation = _location;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Below is the code through which I am trying to update the toLocation binding value to different value from another XAML page as below:
public partial class AvailableLocationsPopUp : PopupPage
{
private static AvailableLocationViewModel obj;
public AvailableLocationsPopUp(WorkItem _workItem)
{
InitializeComponent();
obj = new AvailableLocationViewModel(gWorkItem.itemid);
BindingContext = obj;
}
private void OnClose(object sender, EventArgs e)
{
PopupNavigation.Instance.PopAsync();
}
private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
Location content = e.Item as Location;
ItemPutAway.itemPutAwayViewModel.setLocation("ABC-XYZ");
PopupNavigation.Instance.PopAsync();
}
}
As I mentioned in the discussion, you have to also implement the INotifyPropertyChanged interface of the class WorkItem.
Implement INotifyPropertyChanged in ItemPutAwayViewModel will only help for changes in the WorkItems(like add or remove one WorkItem), not the changes inside the WorkItem.
So, the code should be:
public class WorkItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _toLocation;
public string toLocation
{
get => _toLocation;
set
{
_toLocation = value;
NotifyPropertyChanged();
}
}
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Use XAML UserControl with dataobject as property , as DataTemplate in Gridview - Binding

I have a collection of Objects with dependencyproperties, and Observer patern as I feed realtime data in "AssetIHM" Object.
public class assetVM: DependencyObject ,IObserver
{
public assetVM(AssetIHM aihm)
{
ObserverCollectionSingleton.ObserverCollectionInstance.Add(this);
_aihm = aihm;
}
private string _assetname;
public string assetname
{
get { return _assetname; }
set
{
_assetname = value;
SetValue(assetnameprop, value);
}
}
public static readonly DependencyProperty assetnameprop =
DependencyProperty.Register("assetnameprop", typeof(string),typeof(assetVM), new UIPropertyMetadata(""));
...
I also have a UserControl, Which should display the information contained in the AssetVM object:
public partial class AssetPanel : UserControl
{
public assetVM Asset
{
get
{
return (assetVM)GetValue(assetProperty);
}
set
{
SetValue(assetProperty, value);
}
}
public static DependencyProperty assetProperty = DependencyProperty.Register(
"Asset", typeof(assetVM), typeof(AssetPanel), new PropertyMetadata(null, new PropertyChangedCallback(OnCurrentItemChanged)));
public AssetPanel(assetVM _Asset)
{
Asset = _Asset;
this.DataContext = this;
InitializeComponent();
...
}
public AssetPanel( )
{
this.DataContext = this;
InitializeComponent();
...
}
...
I my main windows, I have a ListBox,
<Window x:Class="ETRportfolio.MainWindow"
DataContext = "{Binding RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UserControls="clr-namespace:ETRportfolio"
Title="MainWindow" Height="1200" Width="1425">
<StackPanel HorizontalAlignment="Left" Height="1153" VerticalAlignment="Top" Width="1400" Margin="10,10,-18,0">
<ListBox x:Name="Gridview" Height="800" >
<ListBox.ItemTemplate>
<DataTemplate>
<UserControls:AssetPanel Asset="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
MY problem is that I would like to feed my Usercontrol with the data contained is the collection of AssetVM.
public partial class MainWindow : Window
{
public static Dispatcher curDispatcher;
public ObservableCollection<assetVM> Datas
{
get
{
return (ObservableCollection<assetVM>)curDispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.DataBind,
(DispatcherOperationCallback)delegate { return GetValue(DataSProperty); },
DataSProperty);
}
set
{
curDispatcher.BeginInvoke(DispatcherPriority.DataBind,
(SendOrPostCallback)delegate { SetValue(DataSProperty, value); },
value);
}
}
public readonly DependencyProperty DataSProperty = DependencyProperty.Register("DataS", typeof(ObservableCollection<assetVM>), typeof(MainWindow), new PropertyMetadata(null, new PropertyChangedCallback(OnCurrentItemChanged)));
public MainWindow()
{
this.DataContext = this;
curDispatcher = this.Dispatcher;
Datas =new ObservableCollection<assetVM>();
InitializeComponent();
Gridview.ItemsSource = Datas;
addasset.addbtn.Click += onclik;
}
When the AssetPanel constructor is created, it doesn't bind with My AssetVM datas. I always pass through the empty constructor.
How Can I do that in XAML?
I guess the problem is there:
ListBox.ItemTemplate>
<DataTemplate>
<UserControls:AssetPanel Asset="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
Thks!
Edit::
I removed this.DataContext = this;
In the UserControl constructor, but the UserControl Constructor called when a DataObject
assetVM
is added to the ObservableCollection Datas, used as datasource for the Listview, is this the empty constructor. but not:
public AssetPanel(assetVM _Asset)
> {
> Asset = _Asset;
> InitializeComponent();
> ValuationInfo.ItemsSource = new List<string> { "%", "Value" };
> AVbox.ItemsSource = Enum.GetValues(typeof(AV)).Cast<AV>();
> }
So it doesn't bind.
Edit2::
private static void OnCurrentItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
AssetPanel instance = (AssetPanel)d;
instance.Asset = (assetVM)e.NewValue;
return;
}
It binds ! :)
Nevertheless, the Registered Dependency properties in the dataobject are not displayed.
This is my Assetpanel xaml:
<UserControl x:Class="ETRportfolio.AssetPanel"
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="152" d:DesignWidth="1400">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border BorderBrush="#FFDE6A6A" BorderThickness="1" Grid.Row="0" Grid.Column="0" Background="lightblue">
<TextBlock x:Name="assetnamebox" TextWrapping="Wrap" Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl }, Path = assetnameprop, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="15"/>
</Border>
...
TextBlock x:Name="assetnamebox" TextWrapping="Wrap" Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl }, Path = assetnameprop,
is not binding with assetVM.assetname VIA the DependencyProperty assetnameprop.
What is wrong there?
thks
Setting a UserControl's DataContext to itself, as done in your constructors by the statements
this.DataContext = this;
effectivly disables binding to properties of inherited DataContexts, like in
<ListBox.ItemTemplate>
<DataTemplate>
<UserControls:AssetPanel Asset="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
where the binding source is the inherited DataContext of the ListBoxItems, i.e. an assetVM instance.
Remove the DataContext assignment from your UserContr's constructors.

Passing a dictionary to a behavior

I have a simple Behavior like that
public class KeyBoardChangeBehavior : Behavior<UserControl>
{
public Dictionary<string, int> DataToCheckAgainst;
protected override void OnAttached()
{
AssociatedObject.KeyDown += _KeyBoardBehaviorKeyDown;
}
protected override void OnDetaching()
{
AssociatedObject.KeyDown -= _KeyBoardBehaviorKeyDown;
}
void _KeyBoardBehaviorKeyDown(object sender, KeyEventArgs e)
{
// My business will go there
}
}
I want to asign value to this dictionary from the view , I call it as following
<UserControl x:Class="newhope2.MainPage"
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:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:Behaviors="clr-namespace:newhope2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Interactivity:Interaction.Behaviors>
<Behaviors:KeyBoardChangeBehavior />
</Interactivity:Interaction.Behaviors>
<Grid x:Name="LayoutRoot" Background="White">
</Grid>
</UserControl>
but how can I pass this dictionary to the behavior from XAML or its code behind
To take a Binding, a property needs to be a DependencyProperty.
You need to define the property, in the Behaviour, like so:
public Dictionary<string, int> DataToCheckAgainst
{
get { return (Dictionary<string, int>)GetValue(DataToCheckAgainstProperty); }
set { SetValue(DataToCheckAgainstProperty, value); }
}
public static readonly DependencyProperty DataToCheckAgainstProperty =
DependencyProperty.Register(
"DataToCheckAgainst",
typeof(Dictionary<string, int>),
typeof(KeyBoardChangeBehavior),
new PropertyMetadata(null));
Use the Visual Studio "propdp" snippet.
Usage is as Adi said, like so:
<Interactivity:Interaction.Behaviors>
<Behaviors:KeyBoardChangeBehavior DataToCheckAgainst="{Binding MyDictionary}" />
</Interactivity:Interaction.Behaviors>
All you need to do is declare the dictionary as a property and then pass it a value via binding.
In the behavior:
public Dictionary<string, int> DataToCheckAgainst { get; set; }
In XAML:
<Interactivity:Interaction.Behaviors>
<Behaviors:KeyBoardChangeBehavior DataToCheckAgainst="{Binding MyDictionary}" />
</Interactivity:Interaction.Behaviors>