Bind control property from the inner control - c#

I have a control that contains another control. InnerControl contains some property like ButtonVisibility. The simplified InnerControl code sample is below
public class InnerControl : ControlBase
{
//...
public Visibility ButtonVisibility
{
get { return (Visibility)GetValue(ButtonVisibility); }
set { SetValue(ButtonVisibility, value); }
}
public static readonly DependencyProperty ButtonVisibility =
DependencyProperty.Register(
"ButtonVisibility",
typeof (Visibility),
typeof (InnerControl),
new PropertyMetadata(Visibility.Collapsed));
}
This is the code of MainControl which contains InnerControl as a Dependency Property
public class MainControl : ControlBase
{
//...
public InnerControl MyInner
{
get { return (InnerControl) GetValue(MyInnerProperty); }
set { SetValue(MyInnerProperty, value); }
}
public static readonly DependencyProperty MyInnerProperty =
DependencyProperty.Register(
"MyInner",
typeof (InnerControl),
typeof (MainControl),
new PropertyMetadata(null, OnMyInnerPropertyChanged));
private static void OnMyInnerPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//...
}
}
I have two different templates for this controls in the Generic.xaml
<Style TargetType="local:InnerControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:InnerControl">
<Grid x:Name="ContainerGrid">
<!-- some markup -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local:MainControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MainControl">
<Grid x:Name="RootGrid">
<Button x:Name="MyButton" Visibility="{TemplateBinding ???}"/>
<ContentControl x:Name="MyInnerControl" Content="{TemplateBinding MyInner}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is the way how could I use this controls
<controls:MainControl>
<controls:MainControl.MyInner>
<controls:InnerControl ButtonVisibility="Visible">
<!-- some markup -->
</controls:InnerControl>
</controls:MainControl.MyInner>
<!-- some markup -->
</controls:MainControl>
I need to change the state of my MainControl after button's click. That's why it is located in MainControl template.
So, could any body gives me an advise how to bind ButtonVisibility from the MyInner to the MyButton on MainControl?

You need to bind directly to the "ButtonVisibility" property of InnerControl, like this:
<Button x:Name="MyButton"
Visibility="{Binding MyInner.ButtonVisibility,
RelativeSource={RelativeSource TemplatedParent}}"/>

Like #james mentioned, you will have to use RelativeSource to bind to either TemplatedParent or to any Type in the visual tree by specifying type like:
<Button x:Name="MyButton" Content="MyButton"
Visibility="{Binding Path=MyInner.ButtonVisibility,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type controls:MainControl}}}"/>

WPF AttachedProperty helps to change the state of your main control from your inner control.

Related

Binding attached property to attached property inside a control template

Here is a working example of my issue: https://github.com/themimcompany/AttachedPropertyIssue
I have a Style for a Button that defines a ControlTemplate. Inside the ControlTemplate I have a TextBlock that has the same attached property. I want to set/bind the TextBlock's attached property to the value of the Button's attached property. The attached property is a simple int value.
How do I get this working? Is this possible to do in UWP? I get errors that don't give me indication on how to fix this. Like Unspecified error...
Here is my Style:
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<TextBlock local:Translation.TranslationModelID="{Binding Path=(local:Translation.TranslationModelID),
RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is my attached property definition:
public class Translation
{
public static int GetTranslationModelID(DependencyObject obj)
{
return (int)obj.GetValue(TranslationModelIDProperty);
}
public static void SetTranslationModelID(DependencyObject obj, int value)
{
obj.SetValue(TranslationModelIDProperty, value);
}
public static readonly DependencyProperty TranslationModelIDProperty =
DependencyProperty.RegisterAttached("TranslationModelID", typeof(int), typeof(FrameworkElement), new PropertyMetadata(0));
}
Here is the button I'm trying to define, notice I want to assign the attached property of the button- then take the value in the ControlTemplate and assign it to the TextBlock's attached property (look at the style):
<Button local:Translation.TranslationModelID="1" />
Explaining once more: I have an attached property assigned to a Button and I would like to assign the value of that Button's attached property to the same attached property of a TextBlockin the ControlTemplate of that Button's Style. It's not working as I expect. I get Unspecified error.. exception at run time. How do I get this working?
Here is a working example of my issue: https://github.com/themimcompany/AttachedPropertyIssue
EDIT:
This approach is giving me a different error. The property 'TranslationModelID' was not found in type 'PassingAttachedPropertyInControlTemplater.Translation'. [Line: 17 Position: 40]
<Page.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<TextBlock local:Translation.TranslationModelID="{TemplateBinding local:Translation.TranslationModelID}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<Button x:Name="btn" local:Translation.TranslationModelID="1" />
</Grid>
I want to assign an attached property of the templated parent to an attached property of an element inside a ControlTemplate using TemplateBinding or regular Binding
For your requirement, you could create Templated Control that inherit button. Then set TextBlock Attached property in the code behind. Please check the follow segment code.
public sealed class CustomButton : Button, IDisposable
{
private long token;
private TextBlock Tbk;
public CustomButton()
{
this.DefaultStyleKey = typeof(CustomButton);
}
private void CallBackMethod(DependencyObject sender, DependencyProperty dp)
{
UpdateAttachedPropertyValue();
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
token = this.RegisterPropertyChangedCallback(AttachedPropertyTest.Translation.TranslationModelIDProperty, CallBackMethod);
Tbk = GetTemplateChild("TBK") as TextBlock;
UpdateAttachedPropertyValue();
}
public void Dispose()
{
this.UnregisterPropertyChangedCallback(AttachedPropertyTest.Translation.TranslationModelIDProperty,token);
}
private void UpdateAttachedPropertyValue()
{
AttachedPropertyTest.Translation.SetTranslationModelID(Tbk, AttachedPropertyTest.Translation.GetTranslationModelID(this));
}
}
Xaml
<Style TargetType="local:CustomButton" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomButton">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<TextBlock x:Name="TBK"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

WPF Custom control binding image source

I have a custom control based on a button, and I put an image inside. I can set the source of the image in the xaml, but if I try and bind it, it doesn't work.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControl">
<Style TargetType="{x:Type local:MyCustomControl}" BasedOn = "{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="pathname"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This works just fine, however if I replace the <Image Source="pathname"/> with <Image Source={Binding MyImage, RelativeSource={RelativeSource Self}}"/>, and reference a Delegate Property in the class, it breaks.
MyCustomControl.cs
public class MyCustomControl : Button
{
static DependencyProperty m_myimage = null;
private DependencyProperty MyImageProperty
{
get
{
return m_myimage;
}
}
public BitmapImage MyImage
{
get
{
return (BitmapImage)GetValue(MyImageProperty);
}
set
{
SetValue(MyImageProperty, value);
}
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
MyImage = new BitmapImage(new Uri(pathname));
}
private static void RegisterDependencyProperties()
{
if (m_myimage == null)
{
m_myimage = DependencyProperty.Register("MyImage",
typeof(BitmapImage), typeof(MyCustomControl), null);
}
}
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
new FrameworkPropertyMetadata(typeof(MyCustomControl)));
RegisterDependencyProperties();
}
}
How can I get it to work?
Figured it out myself after about 2 hours. The xaml binding should look like,
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="{Binding MyImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Note that the binding relative resource went from RelativeSource={RelativeSource Self} to RelativeSource={RelativeSource TemplatedParent}

programmatically set dynamic resource but FrameWorkElement do not update when ResourceDictionary changed?

Im am using a dynamic resource dictionaries to translate my GUI-elements.
The Dictionaries are loaded on startup or could be changed during runtime.
Works very well!
Now i have to add some GUI-elements programmatically ..
But they will not update after changing the translation..
Here is how i "translate" the GUI-elements in XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:tests.Dialogs" x:Class="tests.Dialogs.Dlg_Main"
xmlns:Translator="clr-namespace:tests.Translation"
Title="{DynamicResource DLG_MAIN_TITLE}"
Height="356" Width="711"
/>
This is my MenuItem Class which is added to a ListBox:
public class MenuItem : FrameworkElement
{
public MenuItem (string resourceKey, ClickAction action)
{
Action=action;
this.SetResourceReference(ContentProperty, resourceKey);
}
public void DoAction ()
{
if (Action!=null)
{
Action();
}
}
public delegate void ClickAction ();
private ClickAction Action;
public Object Content
{
get { return (Object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
// Using a DependencyProperty as the backing store for Name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentProperty=DependencyProperty.Register("Content", typeof(Object), typeof(MenuItem), new PropertyMetadata("leer?"));
}
And this is the XAML Style for the ListBox:
<SolidColorBrush x:Key="ItemBrush" Color="Transparent" />
<SolidColorBrush x:Key="SelectedItemBrush" Color="Orange" />
<Style x:Key="RoundedItem" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="ItemBorder" BorderBrush="Transparent" BorderThickness="8,0,0,0" Margin="0" Padding="5" Background="{StaticResource ItemBrush}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="10,0,0,0" Text="{Binding Content.Content, RelativeSource={RelativeSource TemplatedParent}}" FontSize="18" FontWeight="Normal" VerticalAlignment="Center" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
ListBox used like this in XAML:
<ListBox
x:Name="gui_listbox_menu"
ItemContainerStyle="{StaticResource RoundedItem}"
Background="Transparent"
SelectionChanged="gui_listbox_menu_SelectionChanged"
Margin="0,20,0,0"
ItemsSource="{Binding MenuItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Dlg_Main}}}"
/>
After startup the MenuItems have the correct translation, so the Reference works?!
but if i change the language during runtime only the MenuItems still showing the old language!
If i add an Button programmatically and set its ReferenceSource to the DynamicResource its content will change during runtime, too!
Button btn_test=new Button();
btn_test.Content="test";
btn_test.Width=100;
btn_test.Height=100;
gui_tab_settings_grid.Children.Add(btn_test);
btn_test.SetResourceReference(Button.ContentProperty, "DLG_MAIN_TITLE");
But my MenuItems do not change during runtime??!
Any help would be welcome!
I don't know whats the difference, but here is the "new" MenuItem class..
It derives now ListBoxItem ..
public class MenuItem : ListBoxItem
{
public MenuItem (string resourceKey, ClickAction action)
{
Action=action;
this.SetResourceReference(ContentProperty, resourceKey);
}
public void DoAction ()
{
if (Action!=null)
{
Action();
}
}
public delegate void ClickAction ();
private ClickAction Action;
}

WPF Dependency Property In Nested User Control

I have the following WPF Control which is used to Show A Text With An Image Beside it
XAML Code
<UserControl x:Class="WFWorkSpaceWPF.UserControls.StackedImageTextCtl"
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"
Name="StackedImageText"
>
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ElementName=StackedImageText, Path=ImageSource}" />
<TextBlock Text="{Binding ElementName=StackedImageText, Path=Text}" />
</StackPanel>
</Grid>
CS
public partial class StackedImageTextCtl : UserControl
{
public StackedImageTextCtl()
{
InitializeComponent();
}
#region "Properties"
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StackedImageTextCtl), new UIPropertyMetadata(""));
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
#endregion
}
In my project i want to reuse this control in other three user controls where it will be added as a part of these controls, As you see that the StackedImageTextCtl exposes Two Properties (Text and Image Source) that the parent User Controls needs to provide to it and these three controls will take the value from the container window throw XAML, I know that One of the ways to do so is to replicate defining the properties in each of these three user control and Using AddOwner functionality, but i am looking for a more better approach that won't require any repeat in code, can anyone please direct me to such way?.
This is your UserControl:
<UserControl ...>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" Width="16"/>
<TextBlock Text="{Binding Text}" />
</StackPanel>
</UserControl>
and its code:
in addition to Text and ImageSource, there is State which represents the Add/Edit/delete state of this control, and there is StackCtlState which is an attached property and when attached to a FrameworkElement it represents the Add/Edit/delete state of that control.
public StackedImageTextCtl()
{
InitializeComponent();
DataContext = this;
}
//Text Dependency Property
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
//ImageSource Dependency Property
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
//State Dependency Property
public AddEditDelete State
{
get { return (AddEditDelete)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(AddEditDelete), typeof(StackedImageTextCtl), new UIPropertyMetadata(AddEditDelete.Add));
public static AddEditDelete GetStackCtlState(DependencyObject obj)
{
return (AddEditDelete)obj.GetValue(StackCtlStateProperty);
}
public static void SetStackCtlState(DependencyObject obj, AddEditDelete value)
{
obj.SetValue(StackCtlStateProperty, value);
}
public static readonly DependencyProperty StackCtlStateProperty =
DependencyProperty.RegisterAttached("StackCtlState", typeof(AddEditDelete), typeof(StackedImageTextCtl), new UIPropertyMetadata(AddEditDelete.Add));
I also define an enum:
public enum AddEditDelete { Add, Edit, Delete }
In Window xaml:
Each Button or ToggleButton have their attached property StackCtlState set to the desired value and also have their style set to one of styles for Button or ToggleButton.
Then these styles add a StackedImageTextCtl to the contents of styled button/togglebutton in the proper way that resources can be reused. (if you set just Content and don't set Template instead it will only show the contents of the last Button or last ToggleButton) The added StackedImageTextCtl has a State equal to the attached value of its TemplatedParent which is Button or ToggleButton.
At last the style uses Trigger to set the values for Text and Image, based on State of the StackedImageTextCtl.
<Window...
xmlns:myNamespace="clr-namespace:WpfApplication1">
<Window.Resources>
<Style TargetType="{x:Type myNamespace:StackedImageTextCtl}">
<Style.Triggers>
<Trigger Property="State" Value="Add">
<Setter Property="Text" Value="ADD"/>
<Setter Property="ImageSource" Value="/blue_1.jpg"/>
</Trigger>
<Trigger Property="State" Value="Edit">
<Setter Property="Text" Value="EDIT"/>
<Setter Property="ImageSource" Value="/blue_2.jpg"/>
</Trigger>
<Trigger Property="State" Value="Delete">
<Setter Property="Text" Value="DELETE"/>
<Setter Property="ImageSource" Value="/blue_3.jpg"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="stackButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button>
<myNamespace:StackedImageTextCtl State="{TemplateBinding myNamespace:StackedImageTextCtl.StackCtlState}"/>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="stackToggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton>
<myNamespace:StackedImageTextCtl State="{TemplateBinding myNamespace:StackedImageTextCtl.StackCtlState}"/>
</ToggleButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Button Style="{StaticResource stackButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Add"/>
<Button Style="{StaticResource stackButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Edit"/>
<ToggleButton Style="{StaticResource stackToggleButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Delete"/>
</StackPanel>

Bind DependencyProperty in style

I'm sure this is a simple thing to do, and to look up, but I've yet to have any luck. Basically I want to style a WPF Slider such that when used I can give it two additional strings to display.
The new control looks like this:
public class SliderPicker : Slider
{
public string LeftLabel
{
get { return (string)GetValue(LeftLabelProperty); }
set { SetValue(LeftLabelProperty, value); }
}
public static readonly DependencyProperty LeftLabelProperty =
DependencyProperty.Register("LeftLabel", typeof(string), typeof(SliderPicker), new UIPropertyMetadata(String.Empty));
public string RightLabel
{
get { return (string)GetValue(RightLabelProperty); }
set { SetValue(RightLabelProperty, value); }
}
public static readonly DependencyProperty RightLabelProperty =
DependencyProperty.Register("RightLabel", typeof(string), typeof(SliderPicker), new UIPropertyMetadata(String.Empty));
}
The style like this:
<Style x:Key="SlickSlider" TargetType="{x:Type Slider}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
...
<TextBlock Text="{TemplateBinding LeftLabel}" Grid.Row="2" HorizontalAlignment="Left" />
<TextBlock Text="{TemplateBinding RightLabel}" Grid.Row="2" HorizontalAlignment="Right" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And usage:
<controls:SliderPicker LeftLabel="RealPreference" RightLabel="RealCoverage" Width="400" Style="{StaticResource SlickSlider}"/>
This doesn't seem to work. So, how do I set the DP on the control and show it in the template? I thought that's what TemplateBinding was for?
You only need a small fix. Change {x:Type Slider} to {x:Type controls:SliderPicker}
You need to apply the custom control as the type in your style and template. Without it your trying to look for LeftLabelProperty and RightLabelProperty in Slider and not SliderPicker with your template binding.
Change your style to
<Style x:Key="SlickSlider" TargetType="{x:Type controls:SliderPicker}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:SliderPicker}">
...
<TextBlock Text="{TemplateBinding LeftLabel}" Grid.Row="2" HorizontalAlignment="Left" />
<TextBlock Text="{TemplateBinding RightLabel}" Grid.Row="2" HorizontalAlignment="Right" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Have tried this with the class you've posted and it works fine :)

Categories

Resources