Hi i am trying to pass from Button Content to ControlTemplate -> Label Content
How can i do this?
I am just starting to learn xaml and faced such a problem, I would be very grateful for any help.
Code:
<Style x:Key="CloseBtn" TargetType="Button">
<Setter Property="Content" Value="Flex"/> <!---From here --->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Label Name="Border" Focusable="False" Background="#00ff0000" Foreground="#A770FA"
Content="" <--- Over here --->
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>```
Use relative source in your binding to bind to properties on a parent control, in this case, it could be either :
<Label Name="Border" Focusable="False" Background="#00ff0000" Foreground="#A770FA"
Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}},Path=Content}"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
OR
<Label Name="Border" Focusable="False" Background="#00ff0000" Foreground="#A770FA"
Content="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
Relative source also allows you to bind to properties on the same control , ancestors upto a certain level, etc. Please refer to the documentation : https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.relativesource?view=net-5.0
Related
I created HighlightTextBox that derived TextBox. And the code is as shown below.
<Style TargetType="{x:Type host:HighlightTextBox}">
<Setter Property="AcceptsReturn" Value="True" />
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="Foreground" Value="#00000000"/>
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=FontSize, Mode=TwoWay}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="textArea" TargetType="{x:Type host:HighlightTextBox}">
<Border BorderThickness="{Binding BorderTickness}"
BorderBrush="{Binding BorderBrush}"
Background="{Binding BackGround}">
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<host:TextCanvas x:Name="PART_RenderCanvas" ClipToBounds="True"
TextOptions.TextRenderingMode="ClearType" TextOptions.TextFormattingMode="Display"
LineHeight="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=LineHeight}"/>
<ScrollViewer x:Name="PART_ContentHost" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The important point is ControlTemplate.
As you can see, the content of the HighlightTextBox consists of TextCanvas and ScrollViewer.
The HighlightTextBox highlights the currently selected line by painting on the TextCanvas but it invasion the VerticalScrollViewer section as below.
I want to display it by not invasion the VerticalScrollViewer.
I think that maybe the cause is TextCanvas occupy entire section of TextBox.
So I tried to move TextCanvas into the ScrollViewer but this way make facing the run-time error that "PART_ContentHost can't have child element.
I think that another way is to get the content width of the TextBox except for the width of the VerticalScrollBar and binding it to the width of the TextCanvas. But I don't know how to get it.
What I should do to solve this problem?
If you have a better way or another way to solve then please let me know.
Thank you for reading.
Search the visual tree for your ScrollViewer's content presenter, that will give you the width of the content area itself:
var scrollViewer = yourTextBox.Template.FindName("PART_ContentHost", yourTextBox) as ScrollViewer;
var contentPresenter = UIHelper.FindChild<ScrollContentPresenter>(scrollViewer, String.Empty);
var width = contentPresenter.ActualWidth;
UPDATE: You can bind to the ScrollContentPresenter's content control directly like this:
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<TextBlock Text="{Binding ElementName=PART_ContentHost, Path=Content.ActualWidth}" Background="CornflowerBlue" VerticalAlignment="Top"/>
<ScrollViewer x:Name="PART_ContentHost" />
</Grid>
Keep in mind though that this gives you the area of the ScrollViewers content width which, in the example I've given above, will a bit smaller than the TextBlock's width due to the fact that the ScrollViewers content is a TextBoxView with a 2,0,2,0 margin:
To compensate for this you'll probably want to bind your Canvas margin to the TextBoxView margin (which in my case is a TextBlock rather than a Canvas):
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<TextBlock Text="{Binding ElementName=PART_ContentHost, Path=Content.ActualWidth}"
Margin="{Binding ElementName=PART_ContentHost, Path=Content.Margin}"
Background="CornflowerBlue" VerticalAlignment="Top" />
<ScrollViewer x:Name="PART_ContentHost" Padding="0" Margin="0"/>
</Grid>
This will keep your Canvas's alignment in the parent Grid the same as for the TextBoxView so that everything lines up properly:
(You could also just remove the TextBoxView's margin, but that's probably not what you want).
Trying to stay on the MVVM road, I keep struggling with the following task for hours:
I want to show the String-value of a specific Item (in a TextBlock), which is part of the UserCollection (ObservableCollection<Tuple<int, string>>). The selection should take place via the Int-property of the item in the Collection, matching the bound IdCreatedByUser-Property in MyOrder.
To make things more clear:
An UserCollection that holds an ID (int) and NAME (string):
public ObservableCollection<Tuple<int, string>> UserCollection;
A MyOrder-Property holding an Instance of the Orders-Class:
public Order MyOrder;
Here an example of the Orders-class.
public class Order: INotifyPropertyChanged
{
public string Comment;
public int IdCreatedByUser;
public bool IsComplete;
}
Please note that this is just an example for the properties..knowing that get,set are missing here..
The only solution I came up with is to hijack a Combox like this:
<ComboBox ItemsSource="{Binding UserCollection}"
DisplayMemberPath="Item2"
SelectedValue="{Binding MyOrder.IdCreatedByUser}"
SelectedValuePath="Item1">
<ComboBox.Template>
<ControlTemplate>
<TextBlock Text="{Binding SelectedItem.Item2,RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</ControlTemplate>
</ComboBox.Template>
</ComboBox>
The fact that I can use ItemsSource, SelectedValue and SelectedValuePath makes it possible for me to select and show the desired Item. Any solutions for the use of TextBlocks with this one?
I was also thinking about a converter or extra property..but maybe you can show me a way to design this in a better way..
Thanks!
This essentially makes your specialized ComboBox easily reusable. Stuff like FontWeight will be inherited by the ContentPresenter.
<Style x:Key="CollectionLookupComboBox" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<!--
Default to readonly, but you can override that for particular instances
if that's useful somewhere.
-->
<Setter Property="IsReadOnly" Value="True" />
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<Border
x:Name="OuterBorder"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Transparent"
>
<!--
The margin here keeps the text in the same spot when I toggle IsReadOnly,
with the default theme I have. May need to fiddle with that to get it to
look right for you.
-->
<ContentPresenter
Margin="3,2,2,0"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
VerticalAlignment="Stretch"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Example -- this is overkill; you don't need an ItemTemplate or boldface, but it demonstrates how all the usual ComboBox stuff is supported:
<StackPanel
Orientation="Vertical"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<CheckBox
x:Name="ReadOnlyCheckBox"
IsChecked="True"
Margin="1"
Content="Read-Only"
/>
<ComboBox
Margin="1"
Style="{StaticResource CollectionLookupComboBox}"
IsReadOnly="{Binding IsChecked, ElementName=ReadOnlyCheckBox}"
MinWidth="80"
SelectedIndex="0"
FontWeight="Bold"
Foreground="Green"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="DeepSkyBlue" BorderThickness="2" CornerRadius="4">
<Label Content="{Binding}" />
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>First Item</sys:String>
<sys:String>Second Item</sys:String>
</ComboBox>
</StackPanel>
I try to create a ControlTemplate for my Label like this :
<Style TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Grid x:Name="LayoutRoot" Background="White">
<Rectangle Height="30" HorizontalAlignment="Left" Margin="10"
Stroke="transparent"
VerticalAlignment="Top" Width="3"
Fill="#FF259FED" />
<ContentPresenter HorizontalAlignment="Left" Margin="17,0,0,0" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="#7A7E81" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
I want to fill color of rectangle when i create my control, like this for example :
<Label Content="Prénom" VerticalAlignment="Top" CouleurRectangle="#FF259FED" />
So, how can I change Property "Fill" in my controlTemplate to set dynamicly color of rectangle when i create control ?
thank's a lot.
Edit : this is the solution , i create a new class who inherit from Label like this :
Public Class LblTitreChamp
Inherits Label
Public Shared ReadOnly CouleurProperty As DependencyProperty =
DependencyProperty.Register("CouleurRectangle", GetType(SolidColorBrush), GetType(LblTitreChamp))
''' <summary>Propriété pour insérer une couleur au début du Label</summary>
Public Property CouleurRectangle As SolidColorBrush
Get
Return GetValue(CouleurProperty)
End Get
Set(ByVal value As SolidColorBrush)
SetValue(CouleurProperty, value)
End Set
End Property
End Class
then, in my COntrolTemplate :
<Style TargetType="{x:Type local:LblTitreChamp}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:LblTitreChamp}">
<Grid x:Name="LayoutRoot" Background="White">
<Rectangle Height="30" HorizontalAlignment="Left" Margin="10"
Stroke="transparent"
VerticalAlignment="Top" Width="3"
Fill="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Label}}, Path=CouleurRectangle}"/>
<ContentPresenter HorizontalAlignment="Left" Margin="17,0,0,0" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="#7A7E81" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
and finally, to creat a new label :
<my:LblTitreChamp Content="ID" VerticalAlignment="Top" CouleurRectangle="Black" />
thank's a lot for you :)
hi set Fill={TemplateBinding CouleurRectangle} .Hope this will help. And i Expect that you have created a custom Label class That inherit from Label and has DependencyProperty CouleurRectangle.
Since your label is not a CustomControl, then you cannot dynamically make a property on that so the best possible way is to use the background property of label
...
<Rectangle Height="30" ...
Fill="{TemplateBinding Background}" />
...
Else create a CustomControl inheriting the Label and make a new dependency property and also define a XAML template style for it.
I'm putting a WPF application together in which I have an image control which I want to bind a custom command object to from my view model that will execute when the image is clicked. I have exposed the command object from my view model and just need to bind it to the image control.
Is it possible to bind this command object to an image control? If so any advice would be appreciated.
Here's yet another solution I personally love to use most of the time if I want an image with command without enclosing it in another control.
<Image Source="Images/tick.png" Cursor="Hand" Tooltip="Applies filter">
<Image.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding ApplyFilter, Mode=OneTime}" />
</Image.InputBindings>
</Image>
I set its properties Hand and Tooltip so that it's more clear that it's an action and not a static image.
You need to put the image in a button, and bind the button to the command:
<Button Command="{Binding MyCommand}">
<Image Source="myImage.png" />
</Button>
If you don't want the standard button chrome, just change the template of the button with something like that:
<ControlTemplate x:Key="tplFlatButton" TargetType="{x:Type Button}">
<Border Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextElement.Foreground="{TemplateBinding Foreground}"
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontStretch="{TemplateBinding FontStretch}"
TextElement.FontWeight="{TemplateBinding FontWeight}"/>
</Border>
</ControlTemplate>
Note that you will also need to change other properties to override the default button style, otherwise the template above will use the default button background and border:
<Style x:Key="stlFlatButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template" Value="{StaticResource tplFlatButton}" />
</Style>
It can be simpler to avoid using a button and use a Hyperlink instead:
<TextBlock DockPanel.Dock="Top">
<Hyperlink Command="{Binding SomeCommand}">
<Image Source="image.png" />
</Hyperlink>
</TextBlock>
Note that this will render the hyperlink with the default text decoration, so you'll want to add a style that removes that - putting this in the resource dictionary of the containing element will do the trick:
<Style x:Key={x:Type Hyperlink}" TargetType="Hyperlink">
<Setter Property="TextDecorations"
Value="{x:Null}" />
</Style>
Simplified version of answer from #Robert Rossney:
<TextBlock>
<Hyperlink Command="{Binding SomeCommand}" TextDecorations="{x:Null}">
<Image Source="{StaticResource image.png}" Width="16" />
</Hyperlink>
</TextBlock>
The best way to include an image is to use a {StaticResource x}, see WPF image resources
reset control template of the button and use image in control template..
<Button Width="100" Height="100" Command="{Binding SampleCommand}">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Image Stretch="Uniform" Source="Images/tick.png"></Image>
</ControlTemplate>
</Button.Template>
</Button>
I am planning on writing a hierarchical organizational control, similar to an org chart. Several org chart implementations are out there, but not quite fit what I have in mind.
Binding fields in a DataTemplate to a custom object does not seem to work.
I started with a generic, custom control, i.e.
public class NodeBodyBlock : ContentControl
{
public NodeBodyBlock()
{
this.DefaultStyleKey = typeof(NodeBodyBlock);
}
}
It has a simple style in generic.xaml:
<Style TargetType="org:NodeBodyBlock">
<Setter Property="Width" Value="200" />
<Setter Property="Height" Value="100" />
<Setter Property="Background" Value="Lavender" />
<Setter Property="FontSize" Value="11" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="org:NodeBodyBlock">
<Border Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}" CornerRadius="4" BorderBrush="Black" BorderThickness="1" >
<Grid>
<VisualStateManager/> ... clipped for brevity
</VisualStateManager.VisualStateGroups>
<ContentPresenter Content="{TemplateBinding Content}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My plan now is to be able to use this common definition as a base definition of sorts, with customized version of it used to display different types of content.
A simple example would be to use this on a user control with the following style:
<Style TargetType="org:NodeBodyBlock" x:Key="TOCNode2">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=NodeTitle}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
and an instance defined as
<org:NodeBodyBlock Style="{StaticResource TOCNode2}" x:Name="stTest"
DataContext="{StaticResource DummyData}" />
The DummyData is defined as
<toc:Node NodeNumber="mynum" NodeStatus="A"
NodeTitle="INLine Node Title!"
x:Key="DummyData"/>
With a simple C# class behind it, where each of the fields is a public property.
When running the app, the Dummy Data values simply do not show up in the GUI. A trivial test such as
<TextBlock Text="{Binding NodeTitle}" DataContext="{StaticResource DummyData}"/>
works just fine.
Any ideas around where I am missing the plot?
Update: Binding to the datacontext in the definition in generic.xaml works fine, but any binding in the ContentPresenter is lost.
Your control template is missing a binding on the ContentPresenter, it should look like this:-
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
I just ended up using this example as a base:
http://10rem.net/blog/2010/02/05/creating-customized-usercontrols-deriving-from-contentcontrol-in-wpf-4
Not quite sure what I missed, but the example works.