How to link buttons in child user-control to parent. C# / WPF - c#

i have a WPF application that also uses a custom control i designed.
in this custom control i have some buttons which i would like to give some actions in the parent window.
how can i do that?
thanks!

You need to expose the Buttons' Commands properties as dependency properties.
Say you have a Custom Control (which is DIFFERENT from a UserControl), defined like this:
<Style TargetType="{x:Type custom:MyButtonedCtrl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type custom:MyButtonedCtrl}">
<Border BorderThickness="4"
CornerRadius="2"
BorderBrush="Black">
<StackPanel>
<Button Command="{TemplateBinding CommandForFirstButton}"/>
<Button Command="{TemplateBinding CommandForSecondButton}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then in your code behind, you have to expose the 2 dependency properties: CommandForFirstButton and CommandForSecondButton (of type ICommand):
public class MyButtonedCtrl : ContentControl
{
static MyButtonedCtrl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButtonedCtrl), new FrameworkPropertyMetadata(typeof(MyButtonedCtrl)));
}
#region CommandForFirstButton
public ICommand CommandForFirstButton
{
get { return (ICommand)GetValue(CommandForFirstButtonProperty); }
set { SetValue(CommandForFirstButtonProperty, value); }
}
// Using a DependencyProperty as the backing store for CommandForFirstButton. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandForFirstButtonProperty =
DependencyProperty.Register("CommandForFirstButton", typeof(ICommand), typeof(MyButtonedCtrl), new UIPropertyMetadata(null));
#endregion
#region CommandForSecondButton
public ICommand CommandForSecondButton
{
get { return (ICommand)GetValue(CommandForSecondButtonProperty); }
set { SetValue(CommandForSecondButtonProperty, value); }
}
// Using a DependencyProperty as the backing store for CommandForSecondButton. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandForSecondButtonProperty =
DependencyProperty.Register("CommandForSecondButton", typeof(ICommand), typeof(MyButtonedCtrl), new UIPropertyMetadata(null));
#endregion
}
And whenever you want to use your control:
<custom:MyButtonedCtrl CommandForFirstButton="{Binding MyCommand}"
CommandForSecondButton="{Binding MyOtherCommand}"/>
EDIT : For a UserControl:
Declared like this:
<UserControl x:Class="MyApp.Infrastructure.CustomControls.MyButtonedCtrl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="buttonedCtrl">
<Grid>
<Border BorderThickness="4"
CornerRadius="2"
BorderBrush="Black">
<StackPanel>
<Button Command="{Binding CommandForFirstButton, ElementName=buttonedCtrl}"/>
<Button Command="{Binding CommandForSecondButton, ElementName=buttonedCtrl}"/>
</StackPanel>
</Border>
</Grid>
</UserControl>
The code-behind would be:
/// <summary>
/// Interaction logic for MyButtonedCtrl.xaml
/// </summary>
public partial class MyButtonedCtrl : UserControl
{
public MyButtonedCtrl()
{
InitializeComponent();
}
#region CommandForFirstButton
public ICommand CommandForFirstButton
{
get { return (ICommand)GetValue(CommandForFirstButtonProperty); }
set { SetValue(CommandForFirstButtonProperty, value); }
}
// Using a DependencyProperty as the backing store for CommandForFirstButton. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandForFirstButtonProperty =
DependencyProperty.Register("CommandForFirstButton", typeof(ICommand), typeof(MyButtonedCtrl), new UIPropertyMetadata(null));
#endregion
#region CommandForSecondButton
public ICommand CommandForSecondButton
{
get { return (ICommand)GetValue(CommandForSecondButtonProperty); }
set { SetValue(CommandForSecondButtonProperty, value); }
}
// Using a DependencyProperty as the backing store for CommandForSecondButton. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandForSecondButtonProperty =
DependencyProperty.Register("CommandForSecondButton", typeof(ICommand), typeof(MyButtonedCtrl), new UIPropertyMetadata(null));
#endregion
}
And you use it the same way.
Hope this helps!

Related

XAML x:Class Generic

I have this class
public class FeatureTabBase<T> : UserControl, IFeatureTab<T>
where T : BaseModel
{
public string TabGuid { get; set; }
public T FeaturedElement
{
get { return (T)GetValue(FeaturedElementProperty); }
set { SetValue(FeaturedElementProperty, value); }
}
// Using a DependencyProperty as the backing store for FeaturedElement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FeaturedElementProperty =
DependencyProperty.Register("FeaturedElement", typeof(T), typeof(FeatureTabBase<T>), new PropertyMetadata(null));
}
That implements this interface
public interface IFeatureTab<T>
where T : class
{
T FeaturedElement { get; set; }
string TabGuid { get; set; }
}
And this instance from it
public partial class MyClass : FeatureTabBase<MyType>
{
public MyClass()
{
InitializeComponent();
}
}
But don't know how instantiate it on XAML
All I'm trying to do is a generic console that can show some pages for my different kind of items.
I was reading about x:TypeArguments at
https://learn.microsoft.com/en-us/dotnet/desktop-wpf/xaml-services/xtypearguments-directive
But nothing works.
Any Ideas?
Add x:TypeArguments to the UserControl declaration in XAML:
<ctr:FeatureTabBase
x:Class="YourNamespace.MyClass"
x:TypeArguments="local:MyType"
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:ctr="clr-namespace:YourNamespace"
xmlns:local="clr-namespace:YourNamespace"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
...
</ctr:FeatureTabBase>
Here's a control I use which will at least illustrate the concept.
Obviously, this is not going to be cut and paste for whatever it is you have in mind.
An editrow allows me to easily line up a series of labelled controls inside a stackpanel, and add various standardised functionality to the controls I make content.
public class EditRow : ContentControl
{
public string LabelFor
{
get { return (string)GetValue(LabelForProperty); }
set { SetValue(LabelForProperty, value); }
}
public static readonly DependencyProperty LabelForProperty = DependencyProperty.RegisterAttached(
"LabelFor",
typeof(string),
typeof(EditRow));
public string LabelWidth
{
get { return (string)GetValue(LabelWidthProperty); }
set { SetValue(LabelWidthProperty, value); }
}
public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.RegisterAttached(
"LabelWidth",
typeof(string),
typeof(EditRow)
);
public string PropertyWidth
{
get { return (string)GetValue(PropertyWidthProperty); }
set { SetValue(PropertyWidthProperty, value); }
}
public static readonly DependencyProperty PropertyWidthProperty = DependencyProperty.RegisterAttached(
"PropertyWidth",
typeof(string),
typeof(EditRow)
);
public EditRow()
{
this.IsTabStop = false;
}
}
I template this in a resource dictionary. ( There are other options including custom control generic xaml)
<Style TargetType="{x:Type spt:EditRow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type spt:EditRow}">
<Grid Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=LabelWidth, TargetNullValue=2*}"/>
<ColumnDefinition Width="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=PropertyWidth, TargetNullValue=3*}"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=LabelFor}"
HorizontalAlignment="Right"
TextAlignment="Right"
Margin="2,4,0,2"/>
<Border Padding="8,2,8,2" Grid.Column="1" BorderThickness="0">
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource ErrorToolTip}"/>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ErrorToolTip}"/>
<Style TargetType="{x:Type DatePicker}" BasedOn="{StaticResource ErrorToolTip}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage:
<ItemsControl>
<spt:EditRow LabelFor="Name:" >
<TextBox Text="{Binding EditVM.TheEntity.CustomerName,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" />
</spt:EditRow>
<spt:EditRow LabelFor="Address:" >
<TextBox Text="{Binding EditVM.TheEntity.Address1,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" />
</spt:EditRow>
Notice that I have a TextBox as content of each of those editrows, but it could be a datepicker or whatever.
You could bind that content. Then use datatype on viewmodel type for variable datatemplates.
XAML doesnt support generics, so the ctrl:FeatureTabBase will never work. Also, you cannot inherit the XAML part of a UserControl if you derive a new class from an existing UserControl.
You can't use strongly-typed DataTemplates as they only hook up to the concrete class specified in the type. You need to take a different approach. Maybe simplify ?
public class FeatureTab : UserControl
{
public static readonly DependencyProperty FeaturedElementProperty =
DependencyProperty.Register(
"FeaturedElement",
typeof(ModelBase),
typeof(FeatureTab)
, new PropertyMetadata(null));
public string TabGuid { get; set; }
public ModelBase FeaturedElement
{
get => (ModelBase) GetValue(FeaturedElementProperty);
set => SetValue(FeaturedElementProperty, value);
}
}
NOTE This answer is valid for .Net frameworks prior to 4.7, if you point your project to .Net framework 4.7.2 Generics must work on xaml.

How to use another dependency property as a fallback value

I am creating a user control which is a toggle button with image. I have separate dependency properties for image when the toggle button is checked and when the toggle button is not checked.
Xaml:
<ToggleButton IsChecked="{Binding Checked, Mode=TwoWay}">
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{Binding CheckedImage}"></Setter>
<DataTrigger Binding="{Binding IsChecked}" Value="False">
<Setter Property="Source" Value="{Binding UncheckedImage}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</ToggleButton>
Code behind:
public partial class ImageToggleButton : UserControl
{
public ImageToggleButton()
{
InitializeComponent();
this.DataContext = this;
}
public bool Checked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("Checked", typeof(bool), typeof(ImageToggleButton), null);
public ImageSource CheckedImage
{
get { return (ImageSource)base.GetValue(TrueStateImageProperty); }
set { base.SetValue(TrueStateImageProperty, value); }
}
// Using a DependencyProperty as the backing store for TrueStateImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TrueStateImageProperty =
DependencyProperty.Register("CheckedImage", typeof(ImageSource), typeof(ImageToggleButton), null);
public ImageSource UncheckedImage
{
get { return (ImageSource)base.GetValue(FalseStateImageProperty); }
set { base.SetValue(FalseStateImageProperty, value); }
}
// Using a DependencyProperty as the backing store for FalseStateImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FalseStateImageProperty =
DependencyProperty.Register("UncheckedImage", typeof(ImageSource), typeof(ImageToggleButton), null);
}
MainWindow:
<ImageToggleButton Checked="{Binding IsPlaying}" CheckedImage="{DynamicResource PauseIcon}" UncheckedImage="{DynamicResource PlayIcon}">
</ImageToggleButton>
Is it possible to use CheckedImage as a default image so that if I am not providing UncheckedImage, then in both checked and unchecked state will show CheckedImage?
You could register a PropertyChangedCallback for the CheckedImage property that sets the value of the other one:
public static readonly DependencyProperty TrueStateImageProperty =
DependencyProperty.Register("CheckedImage", typeof(ImageSource), typeof(ImageToggleButton),
new PropertyMetadata(new PropertyChangedCallback(OnPropChanged)));
private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ImageToggleButton tb = (ImageToggleButton)d;
if (tb.UncheckedImage == null)
tb.UncheckedImage = (ImageSource)e.NewValue;
}
By the way, you should following the dependency property naming conventions: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/custom-dependency-properties

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));
}

Member is not recognized or accessible for WPF custom control DependencyProperty

Tried on multiple machines, created a new control, which still has the issue, so I'm doing something wrong I'm sure
Here's how I'm using the control:
<controls:Pip Text="1" Color="Blue" />
The properties show up in the suggested properties list, but are marked as not recognized or accessible.
Designer refuses to display it, but it works... compiles and runs without issue
I've tried common solutions posted in threads here: cleared the cache, clean and rebuild, set to AnyCPU build
Class for the control:
/// <summary>
/// Interaction logic for Pip.xaml
/// </summary>
public partial class Pip : UserControl {
/// <summary>
/// Register the Text property of the control
/// </summary>
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(Pip), new PropertyMetadata("#"));
public string Text {
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
/// <summary>
/// Register the Color property of the control
/// </summary>
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(
"Color", typeof(Color), typeof(Pip), new PropertyMetadata(Colors.Blue));
public Color Color {
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public Pip() {
InitializeComponent();
}
}
XAML for it:
<UserControl x:Class="testing.Controls.Pip" x:Name="pip"
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:gbtis.Controls"
mc:Ignorable="d"
Height="30" Width="30">
<UserControl.Background>
<SolidColorBrush Color="{Binding Path=Color, ElementName=pip, UpdateSourceTrigger=PropertyChanged}" />
</UserControl.Background>
<Viewbox>
<Border>
<TextBlock Padding="5" FontWeight="Bold" Foreground="White" Text="{Binding Path=Text, ElementName=pip, UpdateSourceTrigger=PropertyChanged}">
<TextBlock.Effect>
<DropShadowEffect BlurRadius="2" Direction="0" ShadowDepth="0"/>
</TextBlock.Effect>
</TextBlock>
</Border>
</Viewbox>
</UserControl>

How to set WPF behavior property using a data trigger

I am trying to set a WPF behavior property using a style in the following way:
<StackPanel>
<CheckBox Name="IsFemaleChkBox" Content="Is Female ?" />
<TextBlock>
<Hyperlink> <!--setting property directly like this: local:MyHyperLinkBehavior.Salutation="Mr." isn't working either-->
<TextBlock Text="My Hyperlink"/>
<Hyperlink.Style>
<Style TargetType="Hyperlink">
<Setter Property="local:MyHyperLinkBehavior.Salutation" Value="Mr." />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=IsFemaleChkBox}" Value="True">
<Setter Property="local:MyHyperLinkBehavior.Salutation" Value="Miss" />
</DataTrigger>
</Style.Triggers>
</Style>
</Hyperlink.Style>
</Hyperlink>
</TextBlock>
</StackPanel>
And the behavior class code is this:
class MyHyperLinkBehavior : Behavior<Hyperlink>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += AssociatedObject_Click;
}
public static bool GetIsFemale(DependencyObject obj)
{
return (bool)obj.GetValue(IsFemaleProperty);
}
public static void SetIsFemale(DependencyObject obj, bool value)
{
obj.SetValue(IsFemaleProperty, value);
}
// Using a DependencyProperty as the backing store for IsFemale. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsFemaleProperty =
DependencyProperty.RegisterAttached("IsFemale", typeof(bool), typeof(MyHyperLinkBehavior), new PropertyMetadata(false));
public static string GetSalutation(DependencyObject obj)
{
return (string)obj.GetValue(SalutationProperty);
}
public static void SetSalutation(DependencyObject obj, string value)
{
obj.SetValue(SalutationProperty, value);
}
// Using a DependencyProperty as the backing store for Salutation. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SalutationProperty =
DependencyProperty.RegisterAttached("Salutation", typeof(string), typeof(MyHyperLinkBehavior), new PropertyMetadata(default(string)));
void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
{
MessageBox.Show(Convert.ToString(GetValue(SalutationProperty)));
}
}
I can't figure out why this is not working. Or setting a behavior's property using style isn't valid at all ? What is the other way round, if this is not valid.
There are two types of behaviors in WPF:
System.Windows.Interactivity Behaviors, called also Blend Behaviours
These behaviours are classes inherited from System.Windows.Interactivity.Behavior and you can use them by adding to used them by Adding it to Behaviours collection, e.g:
<Rectangle>
<i:Interaction.Behaviors>
<ei:MouseDragElementBehavior />
</i:Interaction.Behaviors>
</Rectangle>
notice, that these behaviors does not have any custom attached properties. OnAttached and OnDetached methods are automatically called.
Pros: Easy to implement
Cons: Does not work with styles (however, it works with ControlTemplates and DataTemplates)
Behaviors implemented as Custom Attached Property
In these behaviors the logic defined in PropertyChangedCallback of the custom attached property.
public static readonly DependencyProperty SalutationProperty =
DependencyProperty.RegisterAttached("Salutation",
typeof(string),
typeof(MyHyperLinkBehavior),
new PropertyMetadata(OnSalutationPropertyChanged));
private static void OnSalutationPropertyChanged(object sender,
DependencyPropertyChangedEventArgs e)
{
//attach to event handlers (Click, Loaded, etc...)
}
Pros: Can be defined in styles, easier to use
Cons: Chatty code, a little more difficult to implement
You are mixing those two types of behaviors together. Choose one and use it! Since you want to use it in style, you shoud choose behavior implemented as custom attached property
I got it working, it was a small miss by me.
I forgot to set the behavior on the hyperlink.
I need to get the property of the attachedObject and not of the
behavior.
Following code works fine:
<StackPanel>
<CheckBox Name="IsFemaleChkBox" Content="Is Female ?" />
<TextBlock>
<Hyperlink>
<TextBlock Text="My Hyperlink"/>
<i:Interaction.Behaviors> <!--Missed setting behavior-->
<local:MyHyperLinkBehavior />
</i:Interaction.Behaviors>
<Hyperlink.Style>
<Style TargetType="Hyperlink">
<Setter Property="local:MyHyperLinkBehavior.Salutation" Value="Mr." />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=IsFemaleChkBox}" Value="True">
<Setter Property="local:MyHyperLinkBehavior.Salutation" Value="Miss" />
</DataTrigger>
</Style.Triggers>
</Style>
</Hyperlink.Style>
</Hyperlink>
</TextBlock>
</StackPanel>
And the behavior:
class MyHyperLinkBehavior : Behavior<Hyperlink>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += AssociatedObject_Click;
}
public static bool GetIsFemale(DependencyObject obj)
{
return (bool)obj.GetValue(IsFemaleProperty);
}
public static void SetIsFemale(DependencyObject obj, bool value)
{
obj.SetValue(IsFemaleProperty, value);
}
// Using a DependencyProperty as the backing store for IsFemale. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsFemaleProperty =
DependencyProperty.RegisterAttached("IsFemale", typeof(bool), typeof(MyHyperLinkBehavior), new PropertyMetadata(false));
public static string GetSalutation(DependencyObject obj)
{
return (string)obj.GetValue(SalutationProperty);
}
public static void SetSalutation(DependencyObject obj, string value)
{
obj.SetValue(SalutationProperty, value);
}
// Using a DependencyProperty as the backing store for Salutation. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SalutationProperty =
DependencyProperty.RegisterAttached("Salutation", typeof(string), typeof(MyHyperLinkBehavior), new PropertyMetadata(default(string)));
void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
{
// Changing "GetValue(SalutationProperty)" to "this.AssociatedObject.GetValue(SalutationProperty)" works
MessageBox.Show(Convert.ToString(this.AssociatedObject.GetValue(SalutationProperty)));
}
}

Categories