I'm trying to make a WPF window with a control realized by an array of RadioButtons, each of which is represented by a picture. I've used a template provided in this example, so the template looks like that:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="Transparent"
CornerRadius="20">
<Image Source="{Binding Path=Content,
RelativeSource={RelativeSource TemplatedParent}}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
Now the problem is that, in order to set the picture on the control, I have to write the whole path to the picture, like that
<RadioButton Name="radioButtonNo" Checked="radioButton_Checked"
Content="C:\Myfolder\MyProject\Resources\MyPic0.png"
Style="{StaticResource Flag}" Width="75"/>
Naturally, I'd wish to be able to write something like
<RadioButton Name="radioButtonNo" Checked="radioButton_Checked"
Content="MyPic0.png"
Style="{StaticResource Flag}" Width="75"/>
but when I do this, all I get is a standard RadioButton with the filename as a caption. What am I doing wrong?
It should be sufficient to provide a correct relative path, like
<RadioButton Content="Resources/MyPic0.png" .../>
Note that image resources in WPF are usually managed as assembly resource files. Their Build Action is set to Resource and they are referenced by Resource File Pack URIs. The XAML parser automatically adds the missing pack://application:,,,/ prefix.
You may as well explicitly create the ImageSource instance that is assigned to the Source property of the Image control in the ControlTemplate:
<RadioButton ...>
<RadioButton.Content>
<BitmapImage UriSource="Resources/MyPic0.png"/>
</RadioButton.Content>
</RadioButton>
Or with an explicitly written Pack URI:
<RadioButton ...>
<RadioButton.Content>
<BitmapImage UriSource="pack://application:,,,/Resources/MyPic0.png"/>
</RadioButton.Content>
</RadioButton>
Related
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've used WPF and IDataErrorInfo in the past apps to display errors to the user via a controltemplate by putting an image in the adorner and adding a tooltip to the image like this;
<Style x:Key="textStyle" TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Orange"
BorderThickness="2"
CornerRadius="4"
SnapsToDevicePixels="True">
<Border.Effect>
<DropShadowEffect BlurRadius="10"
ShadowDepth="0"
Color="Orange" />
</Border.Effect>
<DockPanel>
<Image Width="16"
Height="16"
Margin="-20,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{StaticResource imgError}"
ToolTip="{Binding ElementName=adornedElement,
Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"
ToolTipService.ShowDuration="30000" />
<AdornedElementPlaceholder Name="adornedElement" />
</DockPanel>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
With the appropriate implementation of IDataErrorInfo in the ViewModel and setting Textbox in the view accordingly the image and tooltip are shown;
<TextBox Name="txt"
Grid.Column="0"
Height="40"
Background="Aqua"
Style="{StaticResource textStyle}"
Text="{Binding Path=Text,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" />
<TextBlock Grid.Column="1"
Height="40"
Background="AliceBlue"
Text="{Binding ElementName=txt,
Path=(Validation.Errors).CurrentItem.ErrorContent}" />
The above code displays correctly in my previous apps and shows the error in the image tooltip as confirmed by the Textblock.
However, in my current app which is built using Prism I can't get the Image to display. The TextBlock updates correctly and I can set the error to the TextBox tooltip via a style trigger without any issue. The problem is I can't seem to get the image (or anything else) to display in the Adorner. The Image is not shown and border is not changed.
The difference between previous apps and this is that the view is in a Region in a ContentControl and I've used dependency injection to inject the viewmodel into the view constructor and set the DataContext.
I can't figure out why this doesn't work when it did previously. I think I may need to include an AdornerDecorator somewhere but I'm perplexed as to where having tried it in a few places without success. Any ideas how I can ensure the Adorner is shown?
Used an AdornerDecorator to wrap the element containing the texbox and all works fine.
I've been working in WPF for quite a while, but there's something basic about styling that I just don't get.
How can I set the foreground and background colors for a Menu control? I started with this:
<Menu IsMainMenu="True" Background="#FF3A3A3A" Foreground="White">
<MenuItem Header="_File">
<MenuItem Header="_Exit">
</MenuItem>
</MenuItem>
</Menu>
The foreground color is apparently inherited by the MenuItem, but the background is not. Next attempt:
<Menu IsMainMenu="True" Background="#FF3A3A3A" Foreground="White">
<MenuItem Background="#FF3A3A3A" Header="_File">
<MenuItem Header="_Exit">
</MenuItem>
</MenuItem>
</Menu>
Now the highlight/overlay colors aren't right when the menu is activated, and I don't see an obvious property to set them. In addition, the menu popup has a wide, white border, and I don't see how to change it's color (or size), either.
What am I missing?
You will want to learn more about templates and styles in WPF (XAML really). In XAML, how a control looks and how a control operates are two completely different things. In your example, you may have a Foreground and Background property, but the style\template of the control my not utilize these properties for the display of the control.
Read http://wpftutorial.net/Templates.html and http://wpftutorial.net/TemplatesStyles.html, they will give you a good and quick overview. For a more in depth look, read this: http://msdn.microsoft.com/en-us/library/ee230084.aspx
If you are using Visual Studio 2012 to edit your WPF UI, you can easily create a copy of the style\template the menu control is using, and then edit it. If you are using Visual Studio 2010, you should download and install (it may or may not be free) Expression Blend to edit your XAML UI.
Tip: If you are using Visual Studio 2012, make sure your Document Outline window pane is visible all the time. This is very handy for editing a XAML UI. Mine defaulted to being collapsed on the left side of the program. This pane is visible in Expression Blend by default.
Find the MenuItem control in the Document Outline. Right-click on it and select Edit Template->Edit a Copy...
This will create a copy of the existing look-and-feel of the menu item for you to edit. When you do this, you will be in editing mode of that template, to "pop out" of that mode, click on the little icon on the top left of the Document Outline window.
When editing the template, you can see the layout and design of the template. When a menu item is being as the drop-down part, it's really displayed like a Popup menu (right click menu). Looking through that template, what pops out at me right away is this color resource named SubMenuBackgroundBrush:
<SolidColorBrush x:Key="SubMenuBackgroundBrush" Color="#FFF5F5F5"/>
If you do a search for SubMenuBackgroundBrush you can see that it is used on a part named PART_Popup:
<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" HorizontalOffset="1" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" VerticalOffset="-1">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent">
<Border x:Name="SubMenuBorder" BorderBrush="#FF959595" BorderThickness="1" Background="{StaticResource SubMenuBackgroundBrush}">
<ScrollViewer x:Name="SubMenuScrollViewer" Margin="1,0" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{StaticResource SubMenuBackgroundBrush}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
<Rectangle Fill="#F1F1F1" HorizontalAlignment="Left" Margin="1,2" RadiusY="2" RadiusX="2" Width="28"/>
<Rectangle Fill="#E2E3E3" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
<Rectangle Fill="White" HorizontalAlignment="Left" Margin="30,2,0,2" Width="1"/>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="true" Margin="2" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
This is the popup that you see when you right-click on something that shows a menu, or a dropdown menu. Change the references from: {StaticResource SubMenuBackgroundBrush} to {TemplateBinding Foreground}.
When you run the program, you'll see that the main background of the popup has changed, but the area where the icon is displayed has not. These are all the <Rectangle Fill=" items in the popup control too. Change those also. The last reference to Rectangle looks like its the line splitting the icon and the text, you may not what to change that.
Enjoy the wonderful world of templates. It looks confusing and like a lot of work. It is. But when you get the hang of it, it's a very cool system. It's hard to go back to any other UI system after you get the hang of it.
What am I missing?
Controls are more or less customizable, and there are two levels of customizing a control:
Setting properties like Foreground, Background, etc, in the XAML where you place the control.
Setting the Template in your Style for the control, and creating your own ControlTemplate.
The second is more involved, but it offers much more flexibility in making the control look how you want. If this case, it sounds like that's what you'll need. Check out the default ControlTemplate for Menu and MenuItem. You can copy/paste them and modify as needed.
<Window.Resources>
<Style TargetType="{x:Type Menu}">
<Setter Property="Template">
<ControlTemplate TargetType="{x:Type Menu}">
<!-- your modified template here -->
</ControlTemplate>
</Setter>
</Style>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Template">
<ControlTemplate TargetType="{x:Type MenuItem}">
<!-- your modified template here -->
</ControlTemplate>
</Setter>
</Style>
</Window.Resources>
I have a WPF UserControl, which is simply a Label for whatever else it goes with. E.g, a Label for a TextBox. I want to place this TextBox inside the LabeledControl markup, like this:
<LabeledControl Label="First name">
<TextBox Binding="{FirstName}" />
</LabeledControl>
The reason I want to do this is to style the way controls and their labels look.
I can't find an obvious way to do this. Am I even approaching this the right way? Should I be looking at templates instead?
I'd say that a better option would be to use the built-in HeaderedContentControl, which allows you to specify a Header (your label) and a Content (your text box) property.
You can then specify a ControlTemplate for the HeaderedContentControl to alter the appearance:
<Style x:Key="MyLabelledItemStyle" TargetType="HeaderedContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="HeaderedContentControl">
<StackPanel Orientation="Horizontal">
<ContentControl Content="{TemplateBinding Header}" Margin="2" />
<ContentControl Content="{TemplateBinding Content}" Margin="2" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This example just concatenates the two components horizontally in a StackPanel, but you could do something more complicated if required.
You can then use this in XAML as below:
<HeaderedContentControl Style="{StaticResource MyLabelledItemStyle}" Header="First Name">
<TextBox Text="{Binding FirstName}" />
</HeaderedContentControl>
I am making a template control so that I can have a button with an image that changes when you click it. I also am trying to get text on top of the button that can change at run time. I have the button images and everything working but I can't seem to get that label at runtime so I can change the text. Here is the code in the xaml. I am missing the code behind
<UserControl.Resources>
<ControlTemplate TargetType="{x:Type Button}" x:Key="ActionButton">
<Grid>
<Label Panel.ZIndex="2" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Arial" Name="lblText" Foreground="#5E4421" FontWeight="Bold" FontSize="14">Test</Label>
<Image Name="Normal" Source="/AssaultWare.Controls;component/Replayer/Images/button_off.png"/>
<Image Name="Pressed" Source="/AssaultWare.Controls;component/Replayer/Images/button_on.png"/>
<Image Name="Disabled" Source="/AssaultWare.Controls;component/Replayer/Images/button_off.png" Visibility="Hidden"/>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Resources>
<Button Canvas.Left="471" Canvas.Top="465" Template="{StaticResource ActionButton}" Name="btnRight"/>
Difficult to decipher your question, but I think you just need to change the Label to a ContentControl and bind its Content property to the Button's Content property:
<ContentControl Content="{TemplateBinding Content}" .../>