I have been making my own custom control and I ran into trouble when I wanted to my OwnButton to react as proper component. I would like my component's content be "ownButton" when it is dragged to designer and then there would be a property "Content" in property-list where a programmer would be able to change that text to whatever is needed.
Here is a part of my Generic.xaml file:
<Button x:Name="PART_Button" Grid.Row="1" Margin="1,1,1,1" Template="{StaticResource OwnButtonTemplate}">
<TextBlock Text="ownButton" FontFamily="Tahoma" FontSize="10" HorizontalAlignment="Center" VerticalAlignment="Center" Name="textBlock" />
</Button>
I was able to make a DependencyProperty in OwnButton class, but it isn't linked to Button's content. Here is the code:
private const string defaultContent = "ownButton";
public String Content
{
get { return (String)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(String), typeof(OwnButton), new FrameworkPropertyMetadata(OwnButton.defaultContent, new PropertyChangedCallback(OwnButton.OnContentChanged), new CoerceValueCallback(OwnButton.CoerceContent)));
Would someone be able to help me with my problem?
It is not possible to bind to component's own DependencyProperty. However, finally I found a solution when I used the fact that the component is a Button with a TextBlock in it. My solution doesn't work on the designer as you can guess from the code, but does what it should do when the program is executed.
<Button x:Name="PART_Button" Grid.Row="1" Margin="1,1,1,1" Template="{StaticResource ButtonRandomTemplate}">
<TextBlock Text="{Binding Content, ElementName=PART_Button, UpdateSourceTrigger=PropertyChanged}" FontFamily="Tahoma" FontSize="10" HorizontalAlignment="Center" VerticalAlignment="Center" Name="textBlock" />
I used databinding to the TextBlock's Text-property and now I was able to combine my component's DependencyProperty Content and PART_Button in code.
Related
I have a simple usercontrol with a single DependencyProperty. I am unable to set bindings on this property. I don't get any exceptions but the bindings just disappear.
I cannot begin to see what is going wrong here. It's so simple.
Here's my usercontrol:
XAML:
<UserControl x:Class="Test.Controls.SimpleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="ucThis">
<TextBox Text="{Binding Path=MyString, ElementName=ucThis}" />
</UserControl>
Code:
public partial class SimpleUserControl : UserControl
{
public SimpleUserControl()
{
InitializeComponent();
}
public string MyString
{
get { return (string)GetValue(MyStringProperty); }
set { SetValue(MyStringProperty, value); }
}
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.Register("MyString", typeof(string),
typeof(SimpleUserControl), new UIPropertyMetadata("simple user control"));
}
XAML from a test app:
<StackPanel>
<testControls:SimpleUserControl MyString="{Binding Path=TestString}"
x:Name="simpleUC" />
<Label Content="From control" />
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1"
Visibility="{Binding Path=MyString, ElementName=simpleUC, Converter={StaticResource nullVisConv}}">
<ContentPresenter Content="{Binding Path=MyString, ElementName=simpleUC}" />
</Border>
<TextBlock Text="Value from control is null."
Margin="5"
Visibility="{Binding Path=MyString, ElementName=simpleUC, Converter={StaticResource nullVisConv}, ConverterParameter={custom:BooleanValue Value=True}}" />
<Label Content="From binding" />
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1"
Visibility="{Binding Path=TestString, Converter={StaticResource nullVisConv}}">
<ContentPresenter Content="{Binding Path=TestString}" />
</Border>
<TextBlock Text="Value from binding is null."
Margin="5"
Visibility="{Binding Path=TestString, Converter={StaticResource nullVisConv}, ConverterParameter={custom:BooleanValue Value=True}}" />
<TextBox Text="You can set focus here." />
</StackPanel>
The main window for the test app has a property named TestString, is its own DataContext and implements INotifyPropertyChanged correctly. SimpleUserControl.MyString updates as it should but the property it is bound to (TestString) does not. I have inspected this with Snoop; the binding I set on the SimplerUserControl is just not present at run time. What is happening here?
UPDATE
Okay. So if I specify Mode=TwoWay the binding works. That's great. Can anyone explain to me why it behaves this way?
Thanks.
Working as designed :). DPs default to 1-way. Personally, I would change your DependencyProperty.Register() call to make the DP default to two-way. That way you don't have to specify two-way explicitly every time you use it. You'll notice that the framework typically makes DPs two-way by default when you'd want the property to write back. Just a convienience.
Yes you need to provide TwoWay Mode for Dependency Property:
public string MyString
{
get { return (string)GetValue(MyStringProperty); }
set { SetValue(MyStringProperty, value); }
}
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.Register("MyString", typeof(string),
typeof(UserControl1), new FrameworkPropertyMetadata("simple user control", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
I have this Circle ProgressBar that responds to my Slider value changed:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="530,303,114,303">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="153" Width="155">
<DesignInControl:CircularProgressBar HorizontalAlignment="Center" VerticalAlignment="Center"
SegmentColor="#FF878889" StrokeThickness="8" Percentage="100" />
<DesignInControl:CircularProgressBar Name="circleVale" HorizontalAlignment="Center" VerticalAlignment="Center"
Percentage="{Binding Value, ElementName=slider}" SegmentColor="#026873" StrokeThickness="8" />
</Grid>
</StackPanel>
<Slider x:Name="slider" Maximum="100" Value="0" Width="200" Margin="597,185,227,495" />
<Label Name="lblCircleProgress" Content="{Binding Value, ElementName=slider}" Margin="698,344,284,343" Foreground="White"
How can I change this value in the code-behind?
I tried to give this control a name property (Name="circleVale") but for some reason I cannot reach it in the code-behind.
Your circleVale control should be accessible in the code behind, but apart from that, i think using SetCurrentValue will leave the binding in place.
Can you change any other property of the progressbar from code?
You have the property Percentage of your CircularProgressBar named "circleVale" bound to the slider's Value. In order to change them from the code behind try to set a depdendency property :
public int SliderValue
{
get { return (int)GetValue(SliderValueProperty); }
set { SetValue(SliderValueProperty, value); }
}
// Using a DependencyProperty as the backing store for SliderValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SliderValueProperty =
DependencyProperty.Register("SliderValue", typeof(int), typeof(MainWindow), new PropertyMetadata(0));
private void btnChangeSliderValue_Click(object sender, RoutedEventArgs e)
{
SliderValue = 70;
}
Set the DataContext of your Window in the constructor:
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
And this can be an example for your XAML:
<StackPanel>
<Slider x:Name="slider" Maximum="100" Value="{Binding SliderValue}" Width="200"/>
<Button Click="btnChangeSliderValue_Click" HorizontalAlignment="Center" Content="Change slider value" Margin="5,10"/>
</StackPanel>
This way you don't need to access the slider using its name. Using binding is easier and step by step you can reach MVVM.
I've defined a class name TextColumns.cs, which has a DependencyProperty RichTextBlockContentProperty:
public static readonly DependencyProperty RichTextBlockContentProperty =
DependencyProperty.Register("RichTextBlockContent", typeof(string),
typeof(RichTextColumns), new PropertyMetadata(""));
public string RichTextBlockContent
{
get { return (string)GetValue(RichTextBlockContentProperty); }
set //Debug, but the SetValue won't fire
{
SetValue(RichTextBlockContentProperty, value);
}
}
In the XAML, I use it as
<FlipView x:Name="flipView"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}">
<FlipView.ItemTemplate>
<DataTemplate x:Name="myDataTemplate">
<UserControl Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates">
<ScrollViewer x:Name="scrollViewer" Style="{StaticResource HorizontalScrollViewerStyle}" Grid.Row="1">
<!-- Content is allowed to flow across as many columns as needed -->
<common:RichTextColumns x:Name="richTextColumns" Margin="117,0,117,47"
RichTextBlockContent="{Binding title}">
<RichTextBlock x:Name="richTextBlock" Width="560" Style="{StaticResource ItemRichTextStyle}">
<Paragraph>
<Run x:Name="RunText" FontSize="26" FontWeight="SemiBold" Text="{Binding title}"/>
</Paragraph>
</RichTextBlock>
</common:RichTextColumns>
</UserControl>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
When the page loaded, it's supposed that the RichTextBlockContent will get the value of the Binding "title", while the Binding in the RichTextBlock worked.
Is there something I've missed?
The setter won't get called. If you need to do logic when the value gets set you need to supply a PropertyChanged callback in the PropertyMetadata Constructor
http://msdn.microsoft.com/en-us/library/ms557330.aspx
I have created a Silverlight User Control. The markup is:
<StackPanel Grid.Row="4" Grid.Column="0" Orientation="Horizontal" Width="Auto" Margin="5">
<Button Content="OK" Margin="0,0,5,5" MinWidth="50" Command="{Binding OKCommand}" />
</StackPanel>
The code behind declares a Dependency property 'OKCommand' as:
public ICommand OKCommand
{
get
{
return (ICommand)GetValue(OKCommandProperty);
}
set
{
SetValue(OKCommandProperty, value);
}
}
public static readonly DependencyProperty OKCommandProperty
= DependencyProperty.Register("OKCommand", typeof(ICommand), typeof(TestUserControl), new PropertyMetadata(null, OKCommandProperty_PropertyChangedCallback));
private static void OKCommandProperty_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
Now I want to use the user control on another page where which is the View & the ViewModel defines the command to which I want the OKCommand to be bound. The XAML markup is as such:
<local:TestControl OKCommand="{Binding Path=TestControlOk}"/>
However when I click the button it does not execute anything. Any clues as to what I am doing wrong here.
You need to show the view model that contains the TestControlOk property so we can tell if that's part of the problem.
UserControls do not register themselves as the data context automatically so the binding inside the user control won't have anything to bind to. Do you have
this.DataContext = this;
anywhere in the UserControl codebehind to enable your first binding to actually work?
Alternatively, you can do something like so:
<UserControl .....
x:Name="MyUserControl">
<StackPanel Grid.Row="4" Grid.Column="0" Orientation="Horizontal" Width="Auto" Margin="5">
<Button Content="OK" Margin="0,0,5,5" MinWidth="50"
Command="{Binding OKCommand, ElementName=MyUserControl}" />
</StackPanel>
</UserControl>
Note the ElementName= part of the binding pointing to the root UserControl element in your XAML.
I have a user control with a DependencyProperty that takes a UIElement. So far, so good, the problem is I cannot find the element's children.
I think the problem is my lack of knowledge, could anyone tell me what the problem is and a possible solution?
I have made a small test-program like this
Usercontrol codebehind:
public UIElement TestSendUiElement
{
get { return (StackPanel)GetValue(TestSendUiElementProperty); }
set { SetValue(TestSendUiElementProperty, value); }
}
public static readonly DependencyProperty TestSendUiElementProperty =
DependencyProperty.Register("TestSendUiElement", typeof(StackPanel), typeof(Test), new FrameworkPropertyMetadata(TestSendUiElementPropertyChanged));
private static void TestSendUiElementPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine(VisualTreeHelper.GetChildrenCount((UIElement)e.NewValue));
}
xaml using the usercontrol:
<my:Test >
<my:Test.TestSendUiElement>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBox Height="23" Width="50" Margin="0,0,5,0" />
<TextBox Height="23" Width="125" />
</StackPanel>
</my:Test.TestSendUiElement>
</my:Test>
Output is 0 children. Shouldn't it be 2?
The content is no Initialized so count the object on initialization
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
Console.WriteLine(VisualTreeHelper.GetChildrenCount((UIElement)e.NewValue));
}
And you will get count 2
I think it doesn't work because whatever you assign to the TestSendUiElement DependencyProperty, it won't be part of the VisualTree. So VisualTreeHelper.GetChildrenCount(...) will not work.
As a direct replacement, LogicalTreeHelper should do the trick.
And if you know the type of the object or can , then it's even better to use exposed properties like ItemsControl.Items, ContentControl.Content and etc., with the exception of classes inheriting from Panel (they're LogicalChildren property is internal).
If you are lazy you could also do the following (untested code):
<my:Test.TestSendUiElement>
<ItemsControl Margin="0,2">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<TextBox Height="23" Width="50" Margin="0,0,5,0" />
<TextBox Height="23" Width="125" />
<ItemsControl>
</my:Test.TestSendUiElement>
Then you change the type of the DP property to ItemsControl, and now you can access the children via this.TestSendUIElement.Items. An ItemsControl is probably not as lightweight as a panel, but using the LogicalTreeHelper probably wouldn't be optimal either. Depends on the scenario.