I simply want a button with no background or anything other than plain text. I have done the following and the button does not show up at all:
<UserControl.Resources>
<ControlTemplate x:Key="linkButtons" TargetType="Button">
<TextBlock Foreground="White" FontSize="28" FontFamily="Verdana" Padding="10"></TextBlock>
</ControlTemplate>
</UserControl.Resources>
<Button Template="{StaticResource linkButtons}" Content="Hello World!"/>
This is because the TextBlock inside the Control template does not have a template binding. Make an attribute like this:
<TextBlock Foreground="White" FontSize="28" FontFamily="Verdana" Padding="10" Text="{TemplateBinding Content}" />
Not sure if thats the correct syntax, but thats the concept.
The problem is that a button is designed to have content, not text - it's a kind of ContentControl. So, to display the content, your template should have this in it:
<ContentPresenter x:Name="contentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"/>
OR, you could make a custom control based on button, add a Text property to it, use your current TextBlock control in the template (but with Text="{Binding Text}") and leave the ContentPresenter out of your template. Making a custom control is a little trickier than just making a template for an existing one, but it's really the best way to get exactly what you're going for.
Related
I have a WPF combo box which ItemsSource is ObservableCollection<User>, where User has string Name and bool IsChecked.
Also I have
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}" Width="20" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
, which nicely shows check boxes before each name and allows me to check/uncheck users.
What I need is to make combo box selected item to show not the selected user but all checked usernames separated by comma, ideally (if resultant string is too long) with ellipsis in the middle, i. e. "Alice, Bart...mew, John".
Possible?
A little bit tricky to implement, but certainly can be done.
First of all you'll want to replace the drop-down bar/button with a TextBlock, so you'll need to modify the control's template. Place the edit cursor anywhere inside the parent ComboBox declaration so that the control appears in the Properties panel at the bottom right. In properties, find Miscellaneous -> Template at the bottom, then click on the little down-arrow to the right and select "Convert to new resource". This will template out the control so that you can start editing it.
Next, find the ToggleButton's ContentPresenter inside the ControlTemplate. You'll want to change it's Content binding to point to a property in your view models which I'll call Names:
<!--<ContentPresenter x:Name="contentPresenter" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" Content="{TemplateBinding SelectionBoxItem}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>-->
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" Content="{Binding Names}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
The next change is to also add a binding to your CheckBox Command property called "NamesChangedCommand" which will be called whenever the user changes the state of a CheckBox:
<CheckBox IsChecked="{Binding IsChecked}" Width="20" Command="{Binding RelativeSource={RelativeSource AncestorType=ComboBox}, Path=DataContext.NamesChangedCommand}" />
Then back in your view model, all you have to do is implement this command and have it generate the new name list. Here's how you'd do that in MVVM Toolkit:
[RelayCommand]
public void NamesChanged()
{
this.Names = String.Join(", ",
this.Items
.Where(item => item.IsChecked)
.Select(item => item.Name));
}
[ObservableProperty]
private string names = "";
Result:
I was going to suggest the xceed checkcombobox
https://github.com/xceedsoftware/wpftoolkit/wiki/CheckComboBox
But I notice it's not free for commercial use any more. You could build something of your own based on their code though.
https://github.com/xceedsoftware/wpftoolkit/blob/master/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.Toolkit/CheckComboBox/Implementation/CheckComboBox.cs
The xaml will be in that repo as well.
I have created an extended button with 2 different border styles invoked by triggers in XAML. Both share the same contentpesenter but after changing the border style more than twice the content in the contentpresenter fails to display.
Below is a link to the entire project with a test bed application that demonstrates the issue, I think the issue is somewhere in the XAML below but I cannot see why it breaks:
Sample Button App
<Style.Resources>
<ContentPresenter x:Key="ButtonContent" Margin="5" HorizontalAlignment="Center"
Content="{Binding Content}"/>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Margin="{Binding KeyMargin}">
<Grid Visibility="{Binding RectangleVisibility}">
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=rectBorder}" />
</Grid.OpacityMask>
<Border x:Name="rectBorder"
CornerRadius="{Binding BorderCorners}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{Binding BorderThickness}"/>
<Viewbox Stretch="Fill"
StretchDirection="Both">
<ContentControl Content="{StaticResource ButtonContent}"/>
</Viewbox>
</Grid>
<Grid Visibility="{Binding EllipseVisibility}">
<Ellipse Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{Binding BorderThickness}"
Fill="{TemplateBinding Background}">
</Ellipse>
<Viewbox Stretch="Fill"
StretchDirection="Both">
<ContentControl Content="{StaticResource ButtonContent}"/>
</Viewbox>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
The problem is most likely that you cannot have the same element (the ContentPresenter in this case) in more than one place in the visual tree, and in which one of the two grids it ends up is undefined, i.e., an implementation archetype of WPF.
To get the element duplicated this might work:
<ContentControl Content="{TemplateBinding Content}"/>
or in your case
<ContentControl Content="{TemplateBinding Content}" Margin="5" HorizontalAlignment="Center"/>
instead of a static resource. The <ContentPresenter/> syntax is pretty much an optimized shortcut for that (or you could set x:Shared="False" on the resource, but having a ContentPresenter as a static resource is as far as I know not how it is intended to be used)
If the Button content is a UIElement itself though, it will be used directly itself in the visual tree, i.e., twice and this wont work either. A better solution would be to just have the content once in the control template and change the visual appearance around it, e.g., using a trigger to set the Grid's OpacityMask.
Another remark is that your control template is very tightly bound to where the Button is used, with direct bindings to the current data context, which reduces its reusability. Some easy fixes is to use TemplateBinding instead of Binding for BorderThickness respectively Margin (instead of KeyMargin), since those are existing properties of the Button.
For better reusability and cleaner code you should consider looking into creating a custom control deriving from Button with dependency properties for BorderCorners, the desired visual state (ellipse vs rectangle) etc. You might also want to use triggers to get the mouse-over effects of the button etc. Have fun control templating!
I have been drilling through XAML and trying to puzzle together how it actually works.
I have got the following XAML from one of the sample code I downloaded, an earlier question has explained away a large part of my confusion, however... I am still trying to make sense why the following element has a child element that references another type altogether.
<Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<VisualStateManager.VisualStateGroups>
<!-- Snipped code irrelevant - just various storyboards-->
</VisualStateManager.VisualStateGroups>
<Grid x:Name="InnerGrid"
Opacity="1"
Margin="0,5,0,5"
Background="{StaticResource TransparentColor}">
<ContentPresenter x:Name="ContentPresenter"
Foreground="{StaticResource TransparentColor}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
Looking at the XAML syntax documentation, the VisualStateManager is a PropertyElement, however I am confused because VisualStateGroups do not appear to be a property of Border. I believe the example is correct however, I need someone to explain to me, how is an element that's not a "proper" Child element (as that's what Grid is), be a legit part of the parent element?
VisualStateManager.VisualStateGroups is attached property. Read the following topic, it may help you: http://msdn.microsoft.com/en-us/library/ms749011.aspx
I am working on a WPF project and I started developing the Ribbon area. I just put a RibbonMenuButton and added three RibbonTextBox inside of it. I want these TextBoxes to retrieve some data from the user. Everything is fine so far.
<rb:RibbonMenuButton LargeImageSource="/image.png" Label="Settings" >
<rb:RibbonTextBox Label="Field 01:" Text="{Binding Field01 }" />
<rb:RibbonTextBox Label="Field 02:" Text="{Binding Field02 }" />
<rb:RibbonTextBox Label="Field 03:" Text="{Binding Field03 }" />
</rb:RibbonMenuButton>
My problem is that the RibbonTextBox becomes a menu item, i.e. I can click it and select it.
But I want to avoid this behavior, I just want to have an "unselectable" RibbonTextBox.
Is there a way to achieve that?
Thank you in advance.
I found the solution here:
<Style TargetType="{x:Type rb:RibbonMenuItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type rb:RibbonMenuItem}">
<ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}"
Content="{TemplateBinding Header}" Grid.Column="1"
ContentStringFormat="{TemplateBinding HeaderStringFormat}"
ContentSource="Header"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
VerticalAlignment="Center"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This works pretty well for me.
Those are typically called Labels. But in lieu of using the appropriate control, the RibbonTextBox has both a IsReadOnly propery, which will leave it selectable (ie. for copy/paste) but not editable. Or IsEnabled which will make it completely non-interactive.
I think the IsEnabled will also prevent you from being able to click on it at all, but I'm not 100% sure on that.
I have radiobutton in Silverlight 3 with FontSize=20.
Within the control the radio dialog image forced to the top alignment.
Relative to the text/content the image appears too high. This is not obvious with smaller text sizes however with the large text size the vertical alignment between the text/content and the dialog image looks ugly.
Any ideas on how to vertical-align = middle both the text and the dialog image?
ty
The image inside the RadioButton consists of several Ellipse elements grouped into a Grid element. If you take a look at the RadioButton template (use Expression Blend to edit a copy of the existing template) you will see that the Grid elements VerticalAlignment is set to Top.
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
<Ellipse x:Name="Background" Fill="#FFFFFFFF"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{TemplateBinding BorderThickness}"
Height="14" Margin="1" Width="14"/>
...
The ContentPresenters (where your text is displayed) VerticalAlignment is set to the value you set in the VerticalContentAlignment property.
<ContentPresenter x:Name="contentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Grid.Column="1" Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
To fix your problem you need to set the VerticalAlignment of the Grid element to Middle or use TemplateBinding to set it to the VerticalContentAlignment value.
<Grid HorizontalAlignment="Left"
VerticalAlignment="Middle">
OR
<Grid HorizontalAlignment="Left"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
It appears the best way is to use something like Padding="5,-7,0,0"