UserControl with DataContext and DependencyProperty - c#

I want to bind a DependencyProperty and DataContext to my UserControl. DataContext is working, but Setting the DependencyProperty has no effect. This is my UserControl:
<MyProperty:MyPropertyControl DataContext="{Binding SelectedPerson}" IsEnabled="True"/>
And this is the CodeBehind of my UserControl:
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.Register(
"IsEnabled",
typeof(Boolean),
typeof(MyProperty:MyPropertyControl),
new FrameworkPropertyMetadata()
{
DefaultValue = false,
BindsTwoWayByDefault = false,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
});
public MyPropertyControl()
{
InitializeComponent();
}
public Boolean IsEnabled
{
get { return (Boolean)GetValue(IsEnabledProperty); }
set
{
SetValue(IsEnabledProperty, value);
NotifyPropertyChanged("IsEnabled");
}
}
I can set the property IsEnabled to true or false, but it has no effect.
User Control Code:
<Button Content="Test" Width="100" Height="30" IsEnabled="{Binding IsEnabled}" />

You'll have to set the source object of the Binding to the UserControl, e.g. like this:
<Button ... IsEnabled="{Binding IsEnabled,
RelativeSource={RelativeSource AncestorType=UserControl}}" />

Related

UserControl's PropertyChangedCallback doesn't run

I have got an usercontrol with some dependency property. One of them (ValueProperty) has got a PropertyChangedCallback but it never run.
namespace test
{
public partial class IndicatorLigth : UserControl
{
public IndicatorLigth()
{
InitializeComponent();
DataContext = this;
CurrentBrush = new SolidColorBrush(InactiveColor);
lIndicator.Background = CurrentBrush;
TurnOnValue = true;
Value = true;
}
public static readonly DependencyProperty ActiveColorProperty =
DependencyProperty.Register("ActiveColor", typeof(Color), typeof(IndicatorLigth), new UIPropertyMetadata(Colors.Green));
public Color ActiveColor
{
get { return (Color)GetValue(ActiveColorProperty); }
set { SetValue(ActiveColorProperty, value); }
}
public static readonly DependencyProperty InactiveColorProperty =
DependencyProperty.Register("InactiveColor", typeof(Color), typeof(IndicatorLigth), new UIPropertyMetadata(Colors.Red));
public Color InactiveColor
{
get { return (Color)GetValue(InactiveColorProperty); }
set { SetValue(InactiveColorProperty, value); }
}
private SolidColorBrush _currentBrush;
public SolidColorBrush CurrentBrush
{
get { return _currentBrush; }
set { _currentBrush = value; }
}
public static readonly DependencyProperty TurnOnValueProperty =
DependencyProperty.Register("TurnOnValue", typeof(bool), typeof(IndicatorLigth), new UIPropertyMetadata(true));
public bool TurnOnValue
{
get { return (bool)GetValue(TurnOnValueProperty); }
set { SetValue(TurnOnValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(bool), typeof(IndicatorLigth),
new FrameworkPropertyMetadata(true,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSetColorChanged));
public bool Value
{
get { return (bool)GetValue(ValueProperty); }
set
{
SetValue(ValueProperty, value);
}
}
private void CheckStatus(bool sign)
{
if (sign == TurnOnValue)
CurrentBrush = new SolidColorBrush(ActiveColor);
else CurrentBrush = new SolidColorBrush(InactiveColor);
}
private static void OnSetColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
IndicatorLigth mycontrol = d as IndicatorLigth;
mycontrol.callmyInstanceMethod(e);
}
private void callmyInstanceMethod(DependencyPropertyChangedEventArgs e)
{
CheckStatus((bool)e.NewValue);
lIndicator.Background = CurrentBrush;
}
}
}
And XAML where I use my usercontrol (I use it in another UserControl):
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:test"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
...
<StackPanel Orientation="Vertical">
<Label Content="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.Sign}"/>
<StackPanel>
<local:IndicatorLigth ActiveColor="Thistle" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.Sign}"/>
</StackPanel>
</StackPanel>
The Sign parameter belongs to an IsEnabled bindable property of a ComboBox which not in the XAML code. The label content is correct, it changes when I change combobox enabled status, but my UserControl setter of Value, OnSetColorChanged and callmyInstanceMethod don't fire. Could you tell me what wrong in my code? Thank you very much.
Update: So I was wrong. The code mentioned above is correct. The problem will be occures when I push the stackpanel into a devexpress LayoutGroup HeaderTemplate:
<dxlc:LayoutGroup Orientation="Vertical" VerticalAlignment="Top">
<dxlc:LayoutGroup.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Label Content="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.Sign}"/>
<StackPanel>
<local:IndicatorLigth ActiveColor="Thistle" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.Sign}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</dxlc:LayoutGroup.HeaderTemplate>
</dxlc:LayoutGroup>
Sorry for disturbing you and thank you for advices. I have found the cause of problem. I needn't use <HeaderTemplate><DataTemplate>, I have to use simple <Header> block :)

DependencyProperty for collection of buttons

I want to give the user the opportunity to set a collection of buttons in my CustomControl.
I tried to solve this with ItemsControl like this:
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type cc:MyCustomControl}}, Path=Buttons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command}">
<Image Source="{Binding MyImageSource}"/>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Buttons DependencyProperty:
public static readonly DependencyProperty ButtonsProperty = DependencyProperty.Register(
"Buttons", typeof(IList), typeof(TileGrid), new PropertyMetadata(default(IList)));
public IList Buttons
{
get { return (IList) GetValue(ButtonsProperty); }
set { SetValue(ButtonsProperty, value); }
}
MyButton class:
public class MyButton: Button
{
public ImageSource MyImageSource { get; set; }
}
And how I want to see it in CustomControl implementation:
<cc:MyCustomControl>
<cc:MyCustomControl.Buttons>
<cc:MyButton Command="{Binding SignDocumentsCommand}"
MyImageSource="pack://application:,,,/CommonResources;component/Images/Icons/pen.png"/>
<cc:MyButton Command="{Binding DeleteDocumentsCommand}"
MyImageSource="pack://application:,,,/CommonResources;component/Images/Icons/remove.png"/>
</cc:MyCustomControl.Buttons>
</cc:MyCustomControl>
But it's not working. In live visual tree i see only MyButtons inside ItemsControl. Is this a right approach? Or i need to solve it another way?
The type that you are using for the Button items in the ItemsControl should not be derived from Button. You may use something simple like shown below. It is also not necessary to declare the Buttons property as dependency property. A simple ObservableCollection is sufficient.
public class MyButtonItem : DependencyObject
{
public static DependencyProperty CommandProperty = DependencyProperty.Register(
nameof(Command), typeof(ICommand), typeof(MyButtonItem));
public static DependencyProperty ImageSourceProperty = DependencyProperty.Register(
nameof(ImageSource), typeof(ImageSource), typeof(MyButtonItem));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
}
public partial class MyCustomControl : UserControl
{
public MyCustomControl()
{
InitializeComponent();
}
public ObservableCollection<MyButtonItem> Buttons { get; }
= new ObservableCollection<MyButtonItem>();
}
The controls XAML would just be what you already have:
<ItemsControl ItemsSource="{Binding Buttons,
RelativeSource={RelativeSource AncestorType=UserControl}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command}">
<Image Source="{Binding ImageSource}"/>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You would then add MyButtonItem objects to the Buttons collection:
<cc:MyCustomControl>
<cc:MyCustomControl.Buttons>
<cc:MyButtonItem
Command="{Binding SignDocumentsCommand}"
ImageSource="/CommonResources;component/Images/Icons/pen.png"/>
<cc:MyButtonItem
Command="{Binding DeleteDocumentsCommand}"
ImageSource="/CommonResources;component/Images/Icons/remove.png"/>
</cc:MyCustomControl.Buttons>
</cc:MyCustomControl>

Dependecy property ICommand doesn't work in nested control

I have a UserControl with DependecyProperty:
public static readonly DependencyProperty OpenCommandProperty =
DependencyProperty.Register(
"OpenCommand",
typeof(ICommand),
typeof(BaseRouteFlatView),
new UIPropertyMetadata(null));
public ICommand OpenCommand
{
get { return (ICommand)GetValue(OpenCommandProperty); }
set { SetValue(OpenCommandProperty, value); }
}
In Xaml:
<UserControl x:Name="myUserControl">
<StackPanel>
<Button x:Name="first" Command="{Binding OpenCommand, ElementName=myUserControl}"/> <!--Command works-->
<controls:DropDownButtonControl>
<controls:DropDownButtonControl.DropDownContent>
<Button x:Name="second" Command="{Binding OpenCommand, ElementName=myUserControl}"/> <!--Command doesn't work-->
</controls:DropDownButtonControl.DropDownContent>
</controls:DropDownButtonControl>
</StackPanel>
</abstractions:UserControlBase>
What source I have to specify for working command in second button?
Try the following:
public UserControl()
{
InitializeComponent();
NameScope.SetNameScope(second, NameScope.GetNameScope(this));
}

The databinding of a usercontrol doesn't update the source model

I'm trying to implement a custom textbox which has a placeholder text. The content of the 'FirstName' property of my model appears in the textbox, as intended. The problem I'm having is when I change the text of the textbox, it isn't updated back in the source model. Why is that?
I've tried setting the binding mode to "TwoWay", but it doesn't change anything. Is there something I'm doing wrong?
Edit: Silly me! As it turns out, I had to put Mode="TwoWay" on both bindings, not just the usercontrol's. I'll mark as answered as soon as possible.
Model.cs
public class Student
{
public string FirstName { get; set; }
}
MainWindow.xaml
<grid>
<ui:prettyTextbox Text="{Binding FirstName}" PlaceholderText="#Enter your name">
</grid>
PrettyTextbox.xaml
<UserControl x:Name="prettyTextbox">
<Grid>
<TextBlock Text="{Binding Path=PlaceholderText, ElementName=prettyTextbox}"
Visibility="{Binding Path=Text, ElementName=prettyTextbox, Converter={StaticResource StringLengthToVisibilityConverter}}"/>
<TextBox Text="{Binding Path=Text, ElementName=prettyTextbox, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
PrettyTextbox.xaml.cs
public partial class PrettyTextbox : INotifyPropertyChanged
{
public static readonly DependencyProperty PlaceholderTextProperty =
DependencyProperty.Register("PlaceholderText", typeof (string),
typeof(PrettyTextbox), new FrameworkPropertyMetadata(default(string)));
public string PlaceholderText
{
get { return (string)GetValue(PlaceholderTextProperty); }
set
{
SetValue(PlaceholderTextProperty, value);
OnPropertyChanged();
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string),
typeof(PrettyTextbox), new FrameworkPropertyMetadata(default(string)));
public string Text
{
get { return (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
OnPropertyChanged();
}
}
public PrettyTextbox()
{
InitializeComponent();
}
}
}
You forgot to make the text property bind two way by default, so you need to change this part:
<ui:prettyTextbox Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
or change FrameworkPropertyMetadata of the text property to:
new FrameworkPropertyMetadata
{
DefaultValue = null,
BindsTwoWayByDefault = true
}

Listbox SelectedItem binding to UserControl

I want to bind a ListBox SelectedItem to
This is my code for Listbox in my UserControl.xaml
Style x:Key="listbox" TargetType="ListBox">
<!-- Region Setter Properties -->
<Setter Property="SelectionMode" Value="Single" />
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type program:UserControl}}, Path=Source}" />
`<ListBox Name="ListBox"
Grid.Row="1"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type program:UserControl}},
Path=SelectedIndex}"
SelectedItem="{Binding Path=(program:UserControl.SelectedItem),
RelativeSource={RelativeSource AncestorType={x:Type program:UserControl}}}"
Style="{DynamicResource listbox}" />`
In my UserControl.xaml.cs
public object SelectedItem
{
get { return (object) GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public int SelectedIndex
{
get { return (int) GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
/// <summary>
/// Identifies the <see cref="Selected" /> dependency property.
/// </summary>
public static readonly DependencyProperty SelectedProperty = DependencyProperty.Register(
SelectedPropertyName,
typeof(object),
typeof(TileContainer),
new UIPropertyMetadata(default(object)));
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof (object), typeof (UserControl), new PropertyMetadata(default(object)));
public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register("SelectedIndex", typeof (int), typeof (UserControl), new PropertyMetadata(default(int)));
Normally it works, infact ItemsSource of my ListBox is correctly taken but SelectedIndex and SelectedItem don't work. I looked around the net but i didn't find any solution because maybe they don't have this issue.
I'm compiling with .NET 4.5 .
Thanx!
Update you DependencyProperty like so
public static readonly DependencyProperty SelectedProperty = DependencyProperty.Register(
SelectedPropertyName,
typeof(object),
typeof(TileContainer),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Categories