WPF PropertyMetadata does not work properly - c#

I have a ToggleSwitch UserControl. ToggleSwitch has DependencyProperty called IsChecked and this shows check status of UserControl
ToggleSwitch.xaml.cs :
public bool IsChecked {
get { return ( bool ) GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(ToggleSwitch),
new PropertyMetadata(false, IsCheckedPropertyOnChanged));
private static void IsCheckedPropertyOnChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
if ( obj is ToggleSwitch ) {
var res = ( ( ToggleSwitch ) obj );
res.ChangeStatus();
}
}
At the same time IsChecked status can change when 'GridMouseLeftButtonDown' event occurs (So the user click the ToggleSwitch):
private void GridOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
IsChecked = !IsChecked;
}
The appearance changes with the ChangeStatus function.
Below code shows how I bind the IsChecked in MainWindow.xaml
<local:ToggleSwitch IsChecked="{Binding SelectedPerson.IsActive}"/>
SelectedPersonis a DependencyProperty in MainWindow.xaml.cs.
The IsChecked property changes successfully every time I create a new instance of SelectedPerson.
But once GridOnMouseLeftButtonDown (ToggleSwitch ClickEvent) fires, the IsCheckedPropertyOnChanged no longer invokes.
Small project example to show the problem:
ToggleSwitch.xaml.cs:
public partial class ToggleSwitch : UserControl {
public bool IsChecked {
get { return ( bool ) GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(ToggleSwitch),
new PropertyMetadata(false, IsCheckedPropertyOnChanged));
private static void IsCheckedPropertyOnChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
if ( obj is ToggleSwitch ) {
var res = ( ( ToggleSwitch ) obj );
res.ChangeStatus();
}
}
public ToggleSwitch() {
InitializeComponent();
}
private void ChangeStatus() {
if ( IsChecked ) {
MainContainer.Background = Brushes.Green;
} else {
MainContainer.Background = Brushes.Red;
}
}
private void GridOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
IsChecked = !IsChecked;
}
}
ToggleSwitch.xaml:
<Grid MouseLeftButtonDown="GridOnMouseLeftButtonDown" Width="150" Height="150">
<Border x:Name="MainContainer" Width="150" Height="150" BorderThickness="1" BorderBrush="Black" Background="White"/>
</Grid>
MainWindow.xaml.cs:
public class Person {
public bool IsActive { get; set; }
}
public partial class MainWindow : Window {
public Person SelectedPerson {
get { return ( Person ) GetValue(SelectedPersonProperty); }
set { SetValue(SelectedPersonProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedPerson. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedPersonProperty =
DependencyProperty.Register("SelectedPerson", typeof(Person), typeof(MainWindow));
public MainWindow() {
InitializeComponent();
this.DataContext = this;
SelectedPerson = new Person {
IsActive = true
};
}
private void ButtonOnClicked(object sender, RoutedEventArgs e) {
var change = !SelectedPerson.IsActive;
SelectedPerson = new Person {
IsActive = change
};
}
}
MainWindow.xaml:
<StackPanel Orientation="Vertical">
<local:ToggleSwitch IsChecked="{Binding SelectedPerson.IsActive}" />
<Button Content="ChangeItemSource" Click="ButtonOnClicked" Width="150" Margin="50" />
</StackPanel>

Related

WPF: how do I edit a label when bound values change? [duplicate]

This question already has answers here:
XAML binding not working on dependency property?
(1 answer)
Callback when dependency property recieves xaml change
(2 answers)
Closed 9 months ago.
I'm trying to make a user control in WPF (using xceed). I have a combobox and an integerUpDown.
A label should change its content when one of those changes its value. First label has just a simple binding to the integerUpDown, that's working. Visual Studio created OnPropertyChanged() and getters/setters automatically and I try to use it to bind my result to the second label but it's not working. Nothing happens.
This is my XAML:
<UserControl x:Class="WpfApp2.CardSelectionControl"
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:WpfApp2" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Margin="5" Width="300" Height="400" Background="LightGray">
<TextBox Margin="5" AcceptsReturn="True" FontSize="20" Padding="5" Height="70">placeholding</TextBox>
<ComboBox SelectedIndex="{Binding SelectedIndex}" Background="Red" Margin="5">
<ComboBoxItem Content="card1"></ComboBoxItem>
<ComboBoxItem Content="card2"></ComboBoxItem>
</ComboBox>
<xctk:UIntegerUpDown Margin="5" x:Name="upDown" Value="{Binding Level}" ></xctk:UIntegerUpDown>
<Label Height="50" Content="{Binding Level}"></Label>
<Label Height="50" Content="{Binding Result}" ></Label>
</StackPanel>
</UserControl>
Code behind:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp2
{
public partial class CardSelectionControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Level
{
get { OnPropertyChanged(); return (int)GetValue(LevelProperty); }
set
{
OnPropertyChanged();
SetValue(LevelProperty, value);
}
}
public int SelectedIndex
{
get { OnPropertyChanged(); return (int)GetValue(SelectedIndexProperty); }
set
{
OnPropertyChanged();
SetValue(SelectedIndexProperty, value);
}
}
public int Result
{
get { return (int)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (SelectedIndex == 0)
{ Result = Level * 2; }
else if (SelectedIndex == 1)
{ Result = Level * 3; }
}
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(20));
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(10));
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(0));
public CardSelectionControl()
{
DataContext = this;
InitializeComponent();
}
}
}
I assume you expect your setters to get called, hence why you are calling OnPropertyChanged everywhere. They don't get called so your code won't be executed.
Instead, you can add a callback to your dependency properties so you know when the values get changed:
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level",
typeof(int),
typeof(CardSelectionControl),
new PropertyMetadata(10, new PropertyChangedCallback(OnChanged)));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var cardSelectionControl = (CardSelectionControl)d;
cardSelectionControl.Recalculate();
}
I took the liberty of changing OnPropertyChanged to Recalculate, added a PropertyChangedCallback for the combobox as well and the entire code becomes:
public partial class CardSelectionControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Level
{
get { return (int)GetValue(LevelProperty); }
set { SetValue(LevelProperty, value); }
}
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
public int Result
{
get { return (int)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
public void Recalculate()
{
if (SelectedIndex == 0)
Result = Level * 2;
else if (SelectedIndex == 1)
Result = Level * 3;
}
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(20));
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(10, new PropertyChangedCallback(OnChanged)));
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(0, new PropertyChangedCallback(OnChanged)));
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var cardSelectionControl = (CardSelectionControl)d;
cardSelectionControl.Recalculate();
}
public CardSelectionControl()
{
DataContext = this;
InitializeComponent();
}
}

Unable to bind properties using custom controls in UWP in a ListView

I have a ListView in UWP which displays a list of custom controls CustomControl. Reading around I have seen that other users have face similar issues and their solution mostly revolved around setting the DataContext of their controls, but I cannot understand how I can do that in my example. In order to dynamically update the view I used DependencyProperties in my model which is the following:
public class DataObject : DependencyObject
{
public string Name
{
get { return (string)GetValue(nameProperty); }
set { SetValue(nameProperty, value); }
}
// Using a DependencyProperty as the backing store for name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty nameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DataObject), new PropertyMetadata("Name"));
}
Then in my main page I implemented the following logic to change the Name of my third element:
public sealed partial class MainPage : Page
{
private readonly ObservableCollection<DataObject> dataList;
public MainPage()
{
this.InitializeComponent();
this.dataList = new ObservableCollection<DataObject>();
for (int i = 0; i < 5; i++)
{
DataObject dataObject = new DataObject();
dataObject.Name = "Item " + i.ToString();
this.dataList.Add(dataObject);
}
DataListView.ItemsSource = dataList;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var obj = dataList.ElementAt(2);
obj.Name = "Hello!";
}
}
The XAML for the main page is the following:
<Page
x:Class="ListViewTest.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:controls="using:ListViewTest.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ListView Name="DataListView">
<ListView.ItemTemplate>
<DataTemplate>
<controls:CustomControl DisplayName="{Binding Name}"></controls:CustomControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Button" HorizontalAlignment="Center" Height="92" Width="238"
Click="Button_Click"/>
</Grid>
</Page>
The custom control CustomControl is this:
namespace ListViewTest.Controls
{
public sealed partial class CustomControl : UserControl
{
public string DisplayName
{
get { return (string)GetValue(DisplayNameProperty); }
set {
SetValue(DisplayNameProperty, value);
DisplayText.Text = value;
}
}
// Using a DependencyProperty as the backing store for DisplayName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayNameProperty =
DependencyProperty.Register("DisplayName", typeof(string), typeof(CustomControl), new PropertyMetadata("DisplayText"));
public CustomControl()
{
this.InitializeComponent();
}
}
Its structure is very simple:
<Grid>
<Button Name="ClickButton" Content="Button" Margin="171,165,0,0" VerticalAlignment="Top"/>
<TextBlock Name="DisplayText" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center"/>
</Grid>
The problem is that when I click the button nothing happens and I am struggling to understand why.
DataObject shouldn't inherit from DependencyObject. It should be defined as a CLR object that implements INotifyPropertyChanged:
public class DataObject : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also, the setter of the CLR wrapper for the dependency property in CustomControls should only set the value of the dependency property. You could set the value of the TextBlock using a PropertyChangedCallback:
public sealed partial class CustomControl : UserControl
{
public string DisplayName
{
get { return (string)GetValue(DisplayNameProperty); }
set { SetValue(DisplayNameProperty, value); }
}
public static readonly DependencyProperty DisplayNameProperty =
DependencyProperty.Register(nameof(DisplayName), typeof(string), typeof(CustomControl),
new PropertyMetadata("DisplayText", new PropertyChangedCallback(OnChanged)));
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomControl customControl = (CustomControl)d;
d.DisplayText = e.NewValue as string;
}
public CustomControl()
{
this.InitializeComponent();
}
}

Value is not updating in custom control bindable property - Xamarin forms / Prism

I created a custom control which has an Entry and a Label inside a StackControl. Then I exposed the IsEnabled property of the Entry thorough a bindable property called IsEntryEnabled which I wanted to bind to a bool property of my VM. But it never triggers. Any idea what am I doing wrong here?
Custom control - Xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:effects="clr-namespace:VMSTablet.Effects;assembly=VMSTablet"
x:Class="VMSTablet.Controls.StandardFormEntry">
<StackLayout >
<Label Text="{Binding LabelText}" Style="{StaticResource DefaultLabelStyle}" TextColor="{StaticResource DarkGreenColor}"/>
<Entry x:Name="CustomEntry" Text="{Binding Text}"
IsEnabled="{Binding IsEntryEnabled}"
Keyboard="{Binding Keyboard}"
Behaviors="{Binding Behaviors}"
TextColor="{StaticResource DarkGreenColor}"
Placeholder="{Binding Placeholder}" Style="{StaticResource DefaultEntryStyle}" >
<Entry.Effects>
<effects:EntryBarColorEffect/>
</Entry.Effects>
</Entry>
</StackLayout>
</ContentView>
Custom control - Code Behind
namespace VMST.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class StandardFormEntry
{
public event EventHandler OnTextChanged;
public event EventHandler OnUnfocused;
public event EventHandler OnFocused;
public static readonly BindableProperty PlaceholderProperty =
BindableProperty.Create("Placeholder", typeof(string), typeof(StandardFormEntry), string.Empty);
public static readonly BindableProperty LabelTextProperty =
BindableProperty.Create("LabelText", typeof(string), typeof(StandardFormEntry), string.Empty);
public static readonly BindableProperty EntryTextProperty =
BindableProperty.Create("EntryText", typeof(string), typeof(StandardFormEntry), string.Empty);
public static BindableProperty IsEntryEnabledProperty =
BindableProperty.Create("IsEntryEnabled", typeof(bool), typeof(StandardFormEntry), true);
public static readonly BindableProperty KeyboardProperty =
BindableProperty.Create("Keyboard", typeof(Keyboard), typeof(StandardFormEntry), Xamarin.Forms.Keyboard.Default);
public static readonly BindableProperty BehaviorsProperty =
BindableProperty.Create(nameof(Behaviors), typeof(IList<Behavior>), typeof(StandardFormEntry));
public string LabelText
{
set
{
SetValue(LabelTextProperty, value);
}
get => (string)GetValue(LabelTextProperty);
}
public string EntryText
{
set => SetValue(EntryTextProperty, value);
get => (string)GetValue(EntryTextProperty);
}
public string Placeholder
{
set => SetValue(PlaceholderProperty, value);
get => (string)GetValue(PlaceholderProperty);
}
public bool IsEntryEnabled
{
set
{
SetValue(IsEntryEnabledProperty, value);
}
get => (bool)GetValue(IsEntryEnabledProperty);
}
public Keyboard Keyboard
{
set => SetValue(KeyboardProperty, value);
get => (Keyboard)GetValue(KeyboardProperty);
}
public IList<Behavior> Behaviors
{
set => SetValue(BehaviorsProperty, value);
get => (IList<Behavior>)GetValue(BehaviorsProperty);
}
public StandardFormEntry()
{
InitializeComponent();
BindingContext = this;
CustomEntry.BindingContext = this;
PropertyChanged += StandardFormEntry_PropertyChanged;
CustomEntry.Unfocused += CustomEntry_Unfocused;
CustomEntry.TextChanged += CustomEntry_TextChanged;
CustomEntry.Focused += CustomEntry_Focused;
}
private void StandardFormEntry_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == IsEntryEnabledProperty.PropertyName)
{
**//This never happens!**
}
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
}
private void CustomEntry_Focused(object sender, FocusEventArgs e)
{
OnFocused?.Invoke(sender, e);
}
private void CustomEntry_TextChanged(object sender, TextChangedEventArgs e)
{
OnTextChanged?.Invoke(sender, e);
}
private void CustomEntry_Unfocused(object sender, FocusEventArgs e)
{
OnUnfocused?.Invoke(sender, e);
}
}
}
View Model
I'm trying to trigger the IsEntryEnabled property inside the EditForm() method below. But it doesn't work.
public class PassRegistrationPageViewModel : ViewModelBase
{
public DelegateCommand AddressCommand { get; set; }
public DelegateCommand EditCommand { get; set; }
public DelegateCommand ConfirmCommand { get; set; }
public PassRegistrationPageViewModel(INavigationService navigationService) : base(navigationService)
{
Title = "Page Title";
AddressCommand = new DelegateCommand(ShowBuildings);
EditCommand = new DelegateCommand(EditForm);
ConfirmCommand = new DelegateCommand(ConfirmPass);
//IsQrVisible = false;
}
private bool _isQrVisible;
public bool IsQrVisible
{
get { return _isQrVisible; }
set {
SetProperty(ref _isQrVisible, value);
}
}
private bool _isEditingEnabled;
public bool IsEditingEnabled //Bound to the view below
{
get { return _isEditingEnabled; }
set { SetProperty(ref _isEditingEnabled, value); }
}
private string _text;
public string Text
{
get { return _text; }
set
{
SetProperty(ref _text, value);
}
}
private async void ShowBuildings()
{
await NavigationService.NavigateAsync(nameof(BuildingListPage));
}
public void EditForm()
{
IsEditingEnabled = IsEditingEnabled ? false : true;
}
public void ConfirmPass()
{
}
}
View
I bind the IsEditingEnabled property to IsEntryEnabled property in my custom control which is supposed to trigger the IsEnabled property in the Entry inside. But it never triggers.
<controls:StandardFormEntry IsEntryEnabled="{Binding IsEditingEnabled}" EntryText="{Binding Text}" LabelText="Name" Grid.Column="0" Grid.Row="3"/>
In your custom control, you need to give a name to your content view and refer the name to the source of the binding property, like below:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:effects="clr-namespace:VMSTablet.Effects;assembly=VMSTablet"
x:Class="VMSTablet.Controls.StandardFormEntry"
x:Name="CustomEntryControl">
<StackLayout >
<Label Text="{Binding LabelText}" Style="{StaticResource DefaultLabelStyle}" TextColor="{StaticResource DarkGreenColor}"/>
<Entry x:Name="CustomEntry" Text="{Binding Text}"
IsEnabled="{Binding IsEntryEnabled, Source={x:Reference CustomEntryControl}}"
Keyboard="{Binding Keyboard}"
Behaviors="{Binding Behaviors}"
TextColor="{StaticResource DarkGreenColor}"
Placeholder="{Binding Placeholder}" Style="{StaticResource DefaultEntryStyle}" >
<Entry.Effects>
<effects:EntryBarColorEffect/>
</Entry.Effects>
</Entry>
</StackLayout>

WPF CustomControl hides Children

My CustomControl changes the Visibility of it's children when i assign a value to a child. Here's my code
<UserControl x:Class="CrossProjectUserControls.controls.OnOffSwitch"
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:CrossProjectUserControls.controls"
mc:Ignorable="d" Background="White" Loaded="UserControl_Loaded">
<Grid>
<Image x:Name="imgSwitch" HorizontalAlignment="Left" MaxHeight="30" MinWidth="80" PreviewMouseLeftButtonDown="imgSwitch_PreviewMouseLeftButtonDown" Source="/CrossProjectUserControls;component/res/icons/onoffswitch/switch_off_1_V3_100x40.png" Margin="3,3,0,3"/>
<Label x:Name="lblText" Content="{Binding Text}" Margin="83,3,5,5" VerticalAlignment="Center" FontFamily="Arial" FontSize="16" Background="#FF633C3C"/>
</Grid>
public partial class OnOffSwitch : UserControl {
private BitmapImage bmpOn = new BitmapImage(new Uri(#"/MentorPlus;component/res/icons/onoffswitch/switch_off_1_V3_100x40.png", UriKind.Relative));
private BitmapImage bmpOff = new BitmapImage(new Uri(#"/MentorPlus;component/res/icons/onoffswitch/switch_on_1_V3_100x40.png", UriKind.Relative));
private bool isOn = false;
private string text = "Debug";
public bool IsOn
{
get { return isOn; }
set
{
isOn = value;
ReloadUI();
}
}
public string Text
{
get { return text; }
set { text = value; lblText.Content = text; }
}
public static readonly RoutedEvent SwitchEvent = EventManager.RegisterRoutedEvent(
"Switched", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(OnOffSwitch));
public OnOffSwitch() {
InitializeComponent();
}
private void InitializeControls() {
//this.lblText.Content = text;
//this.imgSwitch.Source = bmpOff;
}
public void Switch() {
IsOn = !IsOn;
}
private void ReloadUI() {
if (this.IsOn == true) {
this.imgSwitch.Source = bmpOn;
} else if (this.IsOn == false) {
this.imgSwitch.Source = bmpOff;
}
}
public event RoutedEventHandler Switched
{
add { AddHandler(SwitchEvent, value); }
remove { RemoveHandler(SwitchEvent, value); }
}
void RaiseSwitchEvent() {
RoutedEventArgs newEventArgs = new RoutedEventArgs(OnOffSwitch.SwitchEvent);
RaiseEvent(newEventArgs);
}
#region Events
private void imgSwitch_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
Switch();
RaiseSwitchEvent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
InitializeControls();
}
#endregion
}
So the Label is totally hidden if i assign a string to it's content like this.lblText.Content = "Test". Is there something I am missing or did I do something wrong?
Thanks for every help

UserControl DependencyProperty binding in ListView

When I try to use a custom UserControl in a ListView, it fails and only empty blocks are displayed (the following TextBlock works though). While the customControl outside the ListView works pretty well. What's the problem?
MainWindow.xaml
<Grid>
<StackPanel>
<controls:CustomControl x:Name="customControl" CustomText="Test"/>
<ListView x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<controls:CustomControl CustomObject="{Binding}"/>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
InitializeMyComponent();
}
public System.Collections.ObjectModel.ObservableCollection<CustomClass> CustomCollection { get; set; }
private void InitializeMyComponent()
{
this.CustomCollection = new System.Collections.ObjectModel.ObservableCollection<CustomClass>();
this.CustomCollection.Add(new CustomClass() { Number = 1, Text = "a" });
this.CustomCollection.Add(new CustomClass() { Number = 2, Text = "b" });
this.CustomCollection.Add(new CustomClass() { Number = 3, Text = "c" });
this.listView.ItemsSource = this.CustomCollection;
this.customControl.Custom = new CustomClass() { Number = 0, Text = "customControl" };
}
}
CustomControl.xaml
<Grid>
<StackPanel>
<TextBlock>
<Run x:Name="numberRun" Text="{Binding CustomObject.Number}"/>
<Run x:Name="textRun" Text="{Binding CustomObject.Text}"/>
<Run Text="{Binding CustomText}"/>
</TextBlock>
</StackPanel>
</Grid>
CustomControl.cs
public partial class CustomControl : UserControl
{
public static readonly DependencyProperty CustomObjectProperty;
public static readonly DependencyProperty CustomTextProperty;
static CustomControl()
{
CustomObjectProperty = DependencyProperty.Register("CustomObject", typeof(CustomClass), typeof(CustomControl), new PropertyMetadata(default(CustomClass), OnCustomObjectPropertyChanged));
CustomTextProperty = DependencyProperty.Register("CustomText", typeof(string), typeof(CustomControl), new PropertyMetadata(string.Empty, OnCustomTextPropertyChanged));
}
public CustomControl()
{
InitializeComponent();
this.DataContext = this;
}
public CustomClass CustomObject
{
get
{
return (CustomClass)(this.GetValue(CustomObjectProperty));
}
set
{
this.SetValue(CustomObjectProperty, value);
}
}
public string CustomText
{
get
{
return (string)(this.GetValue(CustomTextProperty));
}
set
{
this.SetValue(CustomTextProperty, value);
}
}
private static void OnCustomObjectPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { }
private static void OnCustomTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { }
}
CustomClass.cs
public class CustomClass : INotifyPropertyChanged
{
private int number;
private string text;
public CustomClass()
{
this.number = new int();
this.text = string.Empty;
}
public int Number
{
get
{
return this.number;
}
set
{
this.number = value;
this.OnPropertyChanged();
}
}
public string Text
{
get
{
return this.text;
}
set
{
this.text = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string name = null)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Setting a UserControl's DataContext to the UserControl instance like
this.DataContext = this;
effectively prevents bindings to an "external", inherited DataContext, as expected in
<controls:CustomControl CustomObject="{Binding}"/>
As a general rule, never set a UserControl's DataContext explicitly. Remove the above line from the UserControl's constructor, and write the bindings in the UserControl's XAML with RelativeSource:
<Run x:Name="numberRun" Text="{Binding CustomObject.Number,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>

Categories

Resources