Can't make my DependencyProperty work as I expected [duplicate] - c#

This question already has answers here:
Callback when dependency property recieves xaml change
(2 answers)
Closed 4 years ago.
I have a very simple User Control which displays a waiting animation with a text above:
<UserControl x:Class="VNegoceNET.Controls.PleaseWait"
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:VNegoceNET.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="RootElement" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.RowSpan="3" Background="White" Content="" Opacity="0.8"/>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"
Grid.Row="0" FontSize="18" Foreground="Black"
Margin="8" x:Name="Caption" Text="Loading..."/>
<local:SpinningWait Grid.Row="1"/>
</Grid>
</UserControl>
I want to use it like this:
<controls:PleaseWait Text="Jegg Robot"/>
My problem is that it still displays "Loading..." instead of "Jegg Robot", despite my Dependency Property:
public partial class PleaseWait : UserControl
{
public PleaseWait()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(String), typeof(PleaseWait), new PropertyMetadata("Loading in progress..."));
public string Text
{
get => (string)this.GetValue(TextProperty);
set
{
Caption.Text = value;
this.SetValue(TextProperty, value);
}
}
}
What I have missed?

WPF doesn't use common property wrappers for DP (public string Text), it uses SetValue() directly, when property is set from xaml (<controls:PleaseWait Text="Jegg Robot"/>). so code in setter isn't invoked.
What is needed is propertyChangedCallback:
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(PleaseWait),
new PropertyMetadata("Loading in progress...", OnTextChanged));
public string Text
{
get => (string)this.GetValue(TextProperty);
set { this.SetValue(TextProperty, value); }
}
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var c = (PleaseWait) d;
c.Caption.Text = c.Text;
}

Instead of using a PropertyChangedCallback, like ASh mentioned, you can bind the TextProperty of your TextBlock
...
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"
Grid.Row="0" FontSize="18" Foreground="Black"
Margin="8" x:Name="Caption" Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:PleaseWait}}}"/>
...

Related

Fire control event when other control event is fired

This is my first time writing here. I though my first question would by more complex but I am tired of searching for an answer.
I just started with WPF (MVVM), here it goes:
I have 3 user Controls in a Page, the three of them are the same class of Control. The important thing here is that in the first TextBox, when it lost focus, it calls a method to calculate the last TextBox.Text.
<UserControl x:Class="ASSEMBLY.View.UserControls.EtiquetaDinamicaUserControl"
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:ASSEMBLY.View.UserControls"
x:Name="userControl"
mc:Ignorable="d"
Background="Orange" BorderThickness="0" >
<DockPanel VerticalAlignment="Stretch">
<Grid Background="green">
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Viewbox Grid.Column="0" VerticalAlignment="Stretch" Stretch="Fill" >
<Label Grid.Column="0" Content="{Binding NombreEtiqueta, ElementName=userControl, Mode=TwoWay}" HorizontalAlignment="Stretch" BorderThickness="0" Margin="0,0,5,0"
Background="White" VerticalAlignment="Stretch" />
</Viewbox>
<Viewbox Grid.Column="1" VerticalAlignment="Stretch" Stretch="Fill" >
<TextBox Grid.Column="1" MinWidth="30" BorderThickness="0" Margin="0,0,5,0"
VerticalContentAlignment="Center" HorizontalContentAlignment="Left" LostFocus="IdChanged" Loaded="IdChanged"
Text="{Binding IdValue, ElementName=userControl, Mode=TwoWay}"
/>
</Viewbox>
<Viewbox Grid.Column="2" VerticalAlignment="Stretch" Stretch="Fill" >
<TextBox Grid.Row="0" HorizontalAlignment="Stretch" IsEnabled="True" MinWidth="100" BorderThickness="0"
TextAlignment="Left" VerticalAlignment="Stretch"
Text="{Binding Path= Descripcion, ElementName=userControl, Mode=TwoWay}">
</TextBox>
</Viewbox>
</Grid>
</DockPanel>
I have 3 times the same control in the Page, now I need that when the UserControl2 fires LostFocus also fire the LostFocus of the usercontrol3.
<controls:EtiquetaDinamicaUserControl Grid.Row="0" Grid.Column="0" Margin="5,2,0,0" IdValue="{Binding Codempresa1, Mode=TwoWay}" NombreEtiqueta="TEST1"/>
<controls:EtiquetaDinamicaUserControl x:Name="UserControl2" Grid.Row="1" Grid.Column="0" Margin="5,2,0,0" IdValue="{Binding Codempresa2, Mode=TwoWay}" NombreEtiqueta="TEST2"/>
<controls:EtiquetaDinamicaUserControl x:Name="UserControl3" Grid.Row="2" Grid.Column="0" Margin="5,2,0,2" IdValue="{Binding Codempresa3, Mode=TwoWay}" NombreEtiqueta="TEST3"/>
I searched everywhere for something similar, but I found eventrigger (not working because I am not changing a property), interaction (no success trying it).
It works if I set the TextChangedproperty instead of Lostfocus because everything it's bound, but I don't want to be calculating the second textbox every character the user input.
Sorry for my english mistakes and thank you for your help
EDITED. IN ORDER TO HELP I COPY THE CODE BEHIND OF THE USERCONTROL AND THE MVVM PART OF THE VIEW.
USERCONTROL CODE BEHIND:
public static readonly DependencyProperty nombreEtiqueta =
DependencyProperty.Register("NombreEtiqueta", typeof(string), typeof(EtiquetaDinamicaUserControl), new
PropertyMetadata("DEF"));
public string NombreEtiqueta
{
get { return (string)GetValue(nombreEtiqueta); }
set { SetValue(nombreEtiqueta, value); }
}
public static readonly DependencyProperty SetDescripcionProperty =
DependencyProperty.Register("Descripcion", typeof(string), typeof(EtiquetaDinamicaUserControl), new
PropertyMetadata("SIN ASIGNAR"));
public string Descripcion
{
get { return (string)GetValue(SetDescripcionProperty); }
set { SetValue(SetDescripcionProperty, value); }
}
public static DependencyProperty SetIdValueProperty =
DependencyProperty.Register("IdValue", typeof(string), typeof(EtiquetaDinamicaUserControl), new
PropertyMetadata("0"));
public string IdValue
{
get { return (string)GetValue(SetIdValueProperty); }
set { SetValue(SetIdValueProperty, value);
}
}
#region Evento al perder foco IdValue
private void IdChanged(object sender, RoutedEventArgs e)
{
try
{
int tmp_id = Convert.ToInt32(((TextBox)e.Source).Text);
if (tmp_id != 0)
{
Descripcion = tmp_id.ToString();
}
}
catch
{
Descripcion = "SOLO NUMEROS";
}
}
#endregion
MVVM from the View
class PaginaReservaViewModel:ViewModelBase
{
Reserva reserva;
#region Atributos Agencias
private int codempresa1;
private int codempresa2;
private int codempresa3;
#endregion
#region Get/Set Agencias
public int Codempresa1 { get { return codempresa1; } set { codempresa1 = value; } }
public int Codempresa2
{
get { return codempresa2; }
set
{
codempresa2 = value;
if (codempresa3 == 0)
{
codempresa3 = codempresa2;
OnPropertyChanged("Codempresa3");
}
}
}
public int Codempresa3 {
get { return codempresa3; }
set {
codempresa3 = value; } }
Can you use UpdateSourceTrigger="LostFocus" in UserControl' TextBox's Text binding? so it wont fire on every key input. Also please try to implement this all with MVVM (Without hooking events in Code Behind), then you will have much control over the operation.
My Suggestion:
In ViewModel implement a way to modify the other values, when changing Codempresa1. You can include a method to calculate them and call the method in set method of Codempressa1 or you can include the method in PropertyChanged handler.

WPF XAML Binding Not Picking Up DependencyProperty Value

I have a really simple UserControl
<UserControl x:Class="Namespace.Views.TabViews.TabViewTemplate"
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="900" d:DesignWidth="880"
d:DataContext="{d:DesignData Type=TabViewTemplate}">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" BorderBrush="#FFB31B16" BorderThickness="0,0,0,1" Margin="20" VerticalAlignment="Top">
<TextBlock VerticalAlignment="Bottom" FontSize="20" LineHeight="24" Margin="0,0,0,5" Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=Title}"/>
</Border>
<Border Grid.Row="1" Padding="20">
<ContentPresenter ContentSource="Content"/>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
and it's code behind
using System;
using System.Windows;
using System.Windows.Markup;
namespace Namespace.Views.TabViews {
/// <summary>
/// Interaction logic for TabViewTemplate.xaml
/// </summary>
[ContentProperty("Content")]
public partial class TabViewTemplate {
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(TabViewTemplate),
new FrameworkPropertyMetadata(
"Sample",
FrameworkPropertyMetadataOptions.AffectsRender,
OnTitleChanged)
);
private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var control = d as TabViewTemplate;
if (control != null) control.Title = (string)e.NewValue;
}
public TabViewTemplate() {
InitializeComponent();
}
public static void SetTitle(TabViewTemplate element, string value) {
element.SetValue(TitleProperty, value);
}
public static string GetTitle(TabViewTemplate element) {
return (string) element.GetValue(TitleProperty);
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
}
}
I make use of that code like this
<UserControl x:Class="Namespace.Views.TabViews.WelcomeTabView"
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:gif="http://wpfanimatedgif.codeplex.com"
xmlns:tabViews="clr-namespace:Namespace.Views.TabViews"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<tabViews:TabViewTemplate Title="Welcome">
<Grid>
<Border BorderBrush="Coral" BorderThickness="2" Margin="0">
<Image gif:ImageBehavior.AnimatedSource="../../Resources/js_cfg_install_howto.gif"/>
</Border>
</Grid>
</tabViews:TabViewTemplate>
</UserControl>
However the value "Welcome" gets set via the property -- I see this via a breakpoint on the property setter -- however the get is never called. I have used all sorts of Binding combinations, none of which work. What is the "correct" way to set the TextBlock Text by way of a DependencyProperty? Thanks!
It was working. My TextBlock needed a Foreground of Black in order to see the text.

WPF Databinding error on custom control

I am currently getting the following error
System.Windows.Data Error: 40 : BindingExpression path error: 'EthernetView' property not found on 'object' ''LabelTextBox' (Name='Root')'. BindingExpression:Path=EthernetView.SessionName; DataItem='LabelTextBox' (Name='Root'); target element is 'LabelTextBox' (Name='Root'); target property is 'Text' (type 'String')
when I bind data to a custom control like
<controls:LabelTextBox Grid.Column="0" Padding="10,5,10,0" LabelText="Name" Text="{Binding EthernetView.SessionName}"
TextWidth="175" IsReadOnly="True" IsError="False" HorizontalAlignment="Right"/>
I am pretty sure I have my Dependency property on the object set up correctly
public partial class LabelTextBox : UserControl, INotifyPropertyChanged
{
public string Text
{
get { return LblTextBox.Text; }
set
{
SetValue(TextProperty, value);
LblTextBox.Text = value;
PropertyChanged(this, new PropertyChangedEventArgs("Text"));
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text",
typeof(string),
typeof(LabelTextBox),
new PropertyMetadata(default(string), OnTextPropertyChanged));
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LabelTextBox source = d as LabelTextBox;
source.LblTextBox.Text = e.NewValue as string;
}
}
The xaml for the custom control looks like
<UserControl x:Class="Project.LabelTextBox"
x:Name="Root"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition x:Name="TextWidthColumn" Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="LeftLabel" Grid.Column="0" Foreground="Black" Padding="0,0,10,0" VerticalAlignment="Center" FontSize="12"
Text="{Binding LabelText, ElementName=Root, Mode=TwoWay}"/>
<Border x:Name="TextBoxBorder" Grid.Column="1" BorderThickness="2" Background="#FAFAFAFA">
<TextBox x:Name="LblTextBox" VerticalAlignment="Center" Margin="1" FontSize="12" Text="{Binding Text}"
TextChanged="LblTextBox_TextChanged" KeyDown="OnKeyDownHandler"/>
</Border>
<Image x:Name="ErrorImage" Grid.Column="2" Source="pack://application:,,,/Project;component/Images/RedX.ico" Height="12" Width="12"/>
<TextBlock x:Name="RightLabel" Grid.Column="3" Foreground="Black" Padding="10,0,0,0" VerticalAlignment="Center" FontSize="12"
Text="{Binding LabelText, ElementName=Root, Mode=TwoWay}"/>
</Grid>
</UserControl>
The code works properly at runtime, but I would like to clean up the output errors. Can anyone give me some insight on how to fix this error.
Dependency Properties looks like this (GetValue, SetValue!):
public readonly static DependencyProperty IsBusyProperty = DependencyProperty.Register(
"IsBusy", typeof(bool), typeof(BusyControl), new PropertyMetadata(false));
public bool IsBusy
{
get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
so yours is not right.

silverlight nested bindings in usercontrol

I try to bind a bindig example:
settings.xaml
<labels:KeyValueLabel Key="User name:" Label="{Binding Name}" Margin="10"/>
KeyValueLabel.xaml
<UserControl x:Class="rodzic.Controls.Labels.KeyValueLabel"
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"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="100" d:DesignWidth="480">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="KeyLabel" Text="{Binding Key}"/>
<TextBlock x:Name="ValueLabel" Text="{Binding Label}"/>
</Grid>
KeyValueLabel.xaml.cs
public partial class KeyValueLabel : UserControl
{
public string Key { get; set; }
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
"Label",
typeof(String),
typeof(KeyValueLabel),
new PropertyMetadata("default", new PropertyChangedCallback(LabelChanged))
);
static void LabelChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
//e is always being set to ""
}
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public KeyValueLabel()
{
InitializeComponent();
}
}
If I pass value as a static text in settings.xaml it works but binding data from settings.xaml don't for key nor for label.
How to fix label binding? I want to make it work no matter how nested the binding is.
It appears that you want to create a Custom Control, not UserControl. These are two completely different things. With a custom control, you can create your dependency properties and then bind to them directly like you would any other control.
In the custom control, you then catch the setting of it and set the Text property of the TextBlock directly. Thus you don't need the double binding.

Caliburn.micro and a usercontrol click handler

I have created a very simple user control, an ImageButton
<UserControl x:Class="SampleApp.Controls.ImageButton"
Name="ImageButtonControl"
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"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Button>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="6*" />
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Image Grid.Row="1" Source="{Binding ElementName=ImageButtonControl, Path=Image}" VerticalAlignment="Stretch" HorizontalAlignment="Center" />
<TextBlock Grid.Row="2" Text="{Binding ElementName=ImageButtonControl, Path=Text}" VerticalAlignment="Stretch" HorizontalAlignment="Center" />
</Grid>
</Button>
</UserControl>
With code behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace SampleApp.Controls
{
/// <summary>
/// Interaction logic for ImageButton.xaml
/// </summary>
public partial class ImageButton : UserControl
{
public ImageButton()
{
InitializeComponent();
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ImageButton), new UIPropertyMetadata(""));
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null));
}
}
Now I want to use that in my little sample application like
xmlns:controls="clr-namespace:SampleApp.Controls"
<controls:ImageButton Grid.Row="1"
Grid.Column="1"
Margin="2"
Image="/Images/link.png"
Text="DoSomething">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="DoSomething" />
</i:EventTrigger>
</i:Interaction.Triggers>
</controls:ImageButton>
If I give it a x:Name, like
<controls:ImageButton x:Name="DoSomething"
e.g. DoSomething the method DoSomething with that name is directly called when the view is shown, i.e. when I active the viewmodel that contains that button, just like I click the Button (if it was a normal button and not a usercontrol, it would work that way), but the button-click handler is never called on clicking.
Now I tried to add an ActionMessage as seen above, but it does not work either...
What is wrong here?
That's because there is no convention configured for your user control type. You could either add a convention via the ConventionManager (see http://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Conventions), or you could derive your type from Button instead.
You could also not use a custom user control and instead just add the image to the Content property of the Button in your view.

Categories