Using a local resource defined on a ContentControl - wpf

I have a local resource that I want to use on a control. I'd like to define the resource directly on the control since that's the only place it's going to be used.
<Button Background="{StaticResource MyBrush}">
<Button.Resources>
<SolidColorBrush x:Key="MyBrush" Color="#FFD71526"/>
</Button.Resources>
</Button>
This doesn't work MyBrush hasn't been defined at the time Background is set. It would work as a DynamicResource, but I want to keep it static.
Question: How can I set a property on a control using a resource that is defined directly on the control?
I tried setting the property after creating the resource, but if it's possible I can't seem to find the correct syntax.
<Button>
<Button.Resources>
<SolidColorBrush x:Key="MyBrush" Color="#FFD71526"/>
</Button.Resources>
<Button.Background>
{StaticResource MyBrush}
</Button.Background>
</Button>

The correct syntax is the following one:
<Button>
<Button.Resources>
<SolidColorBrush x:Key="MyBrush" Color="#FFD71526"/>
</Button.Resources>
<Button.Background>
<StaticResource ResourceKey="MyBrush" />
</Button.Background>
</Button>

if you do not want to use the color anywhere else there is no point in making a static ressource in the first place!
If it is one time use just put it in the property directly like this:
<Button Background="#FFD71526">
</Button>
Defining a color as a static ressource only makes sense on a parent with multible childs that plan to reuse that property
Edit:
You can use this for your purpose, set the backround once and bind all other properties (for example BorderBrush) to the background like so:
<Button
Background="#FFD71526"
BorderBrush="{Binding RelativeSource={RelativeSource Self}, Path=Background}"
BorderThickness="5">
</Button>

Related

How to get a property of a DynamicResource in XAML?

I have a Brush defined in a code file and I am able to reference it using the DynamicResource extention in XAML at runtime. What I would like to do is to grab the Brush.Color and bind it to an element.
I've tried the approach bellow,
<SolidColorBrush Color="{DynamicResource ButtonHoverTopBrush.Color}" Opacity="0" />
but it doesn't work. How grab that Color?
Try this:
<SolidColorBrush Color="{Binding Color, Source={StaticResource ButtonHoverTopBrush}}"
Opacity="0" />
It doesn't work with DynamicResource instead of StaticResource but if you change the Color of ButtonHoverTopBrush dynamically, it will affect the above brush. You cannot replace the Brush itself though.

Why does my Ribbon Menu Button Popup disappear when I apply this Style?

Here is my XAML:
<Ribbon x:Name="ribbonMain" Height="200" ContextMenu="{x:Null}" VerticalAlignment="Top" ShowQuickAccessToolBarOnTop="False" >
<RibbonTab x:Name="ribbonTabMain" Header="Test Tab" ContextMenu="{x:Null}" >
<RibbonGroup x:Name="ribbonGroupMain" Header="Test Group" ContextMenu="{x:Null}">
<RibbonButton x:Name="ribbonButtonMain" Label="Test Button" ContextMenu="{x:Null}" />
</RibbonGroup>
<RibbonGroup x:Name="ribbonGroupMain2" Header="Test Group 2" ContextMenu="{x:Null}">
<RibbonMenuButton ContextMenu="{x:Null}" Name="ribbonMenuButtonMain" Label="Menu Button">
<RibbonMenuItem ContextMenu="{x:Null}" Name="ribbonMenuItemMain" Header="Menu Item"></RibbonMenuItem>
<RibbonMenuItem ContextMenu="{x:Null}" Name="ribbonMenuItemMain2" Header="Menu Item 2"></RibbonMenuItem>
</RibbonMenuButton>
</RibbonGroup>
</RibbonTab>
</Ribbon>
I then run this C# Code to get the Ribbon Menu Button Default Control Template:
string ribbonMenuButtonControlTemplate = XamlWriter.Save(ribbonMenuButtonMain.Template);
After that I set the x:Name and x:Key properties of the Control Template to something and then put that string of XAML in this:
<Style TargetType="RibbonMenuButton"
<Setter Property="Template">
<Setter.Value>
{DefaultControlTemplateHere}
</Setter.Value>
</Setter>
</Style>
Last I put that Style in my <Window.Resources>.
I wanted to alter the Style from there, but then I realized that the popup just wasn't working anymore.
I expected nothing to change. Seems I was mistaken.
Why does this happen?
Note:
I've tried running this code to see if the popup would open:
if (!ribbonMenuButtonMain.IsDropDownOpen)
{
ribbonMenuButtonMain.IsDropDownOpen = true;
}
With no Style applied that code runs fine and the popup opens.
But with the Style I get this exception:
System.InvalidOperationException: 'This Visual is not connected to a
PresentationSource.'
The XamlWriter.Save method has some serialization limitations that are mentioned here. One of them being that;
Common references to objects made by various markup extension formats, such as StaticResource or Binding, will be dereferenced by the serialization process. These were already dereferenced at the time that in-memory objects were created by the application runtime, and the Save logic does not revisit the original XAML to restore such references to the serialized output.
So your generated template is missing a TemplateBinding to the IsOpen property of the Popup:
<Popup ... IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropDownOpen}">
You may extract the default template including any bindings from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF\System.Windows.Controls.Ribbon.dll using a decompiler such as for example dotPeek.

WPF Command Binding ItemsControl in Styles

I have a style in the Textboxstyles.xaml as following
<Style x:Key="EmptyItemsControlUsabilityDashboard2017Style" TargetType="ItemsControl">
<Style.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Image Height="12" Width="12" Source="/YoLo;component/Resources/Images/link.png" Margin="0,3,0,0" />
<TextBlock x:Name="EmptyCollectionTextBox" Text="{x:Static UsabilityDashboard2017Loc:DashboardUsability2017Resource.lblNumNotDefined}"
Style="{StaticResource UsabilityDashboard2017TextBoxStyle}"
HorizontalAlignment="Center"
Margin="5,25,0,25"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
and I have used it inside another Xaml file as following
<ItemsControl ItemsSource="{Binding YoLoViewModelsCollection}" Name="YoLoViewModelsItemSource" Style="{StaticResource EmptyItemsControlUsabilityDashboard2017Style}">
Now it shows a text box that the this collection is empty but how can I set command bindings on the text block named "EmptyCollectionTextBox" inside the style that user user click it executes a command?
I have already seen the custom commands but somehow they are not working.
There's actually a lot of stuff wrong with this code. First of all I have no idea what that trigger is supposed to be doing, it looks like the controltemplate will only be set if there are no elements in the list?
Secondly, it looks like you're trying to represent each element in the collection with an image and text, all in an ItemsControl. You don't do that by templating the entire control, you do it by templating the ItemTemplate. And you use a DataTemplate, not a ControlTemplate:
Now going back to your actual question, you want notification whenever the TextBlock is clicked on. There are a multitude of different ways to do this, but for this case you may as well replace that TextBlock with a Button, and then override the Template with a ControlTemplate that represents it as a TextBlock. This gives you the best of both worlds: from the GUI's perspective it's really still a TextBlock, but you still get all the button click notifications and Command handler etc:
<Image />
<Button Command="{Binding ClickedCommand}" Cursor="Hand">
<Button.Template>
<ControlTemplate>
<TextBlock Text="Text Binding Goes Here" />
</ControlTemplate>
</Button.Template>
</Button>
This particular example assumes that the items in your collection have a command handler called "ClickedCommand". In practice your handler might reside in the parent class (e.g. the main window's view model), in which case you'd need to give your main window a x:Name (e.g. "_this") and bind to that instead, passing the item in as the CommandParameter so it knows which one was clicked:
<Button Command="{Binding ElementName=_this, Path=DataContext.ClickedCommand}" CommandParameter="{Binding}" Cursor="Hand">
And then your main view model has a handler that looks something like this:
public ICommand ClickedCommand { get { return new RelayCommand<YourCollectionItemType>(OnClicked); } }
private void OnClicked(YourCollectionItemType item)
{
}

Setting Canvas to a control template?

I have a Canvas in a ResourceDictionary xaml file like so:
<Canvas x:Key="Icon">
<Path ... />
<Path ... />
</Canvas>
In my code-behind I load this icon using
LayoutGrid.Children.Add(FindResource("Icon") as Canvas);
And this works fine. Now I want to create a button that uses the same icon as a template. So I create a control template:
<ControlTemplate x:Key="IconTemplate">
...
</ControlTemplate>
Now here's the problem: How would I put the "Icon" resource canvas into the control template? As far as I know, Canvas does not have a Style or Template property. It has a Children property, but it's not accessible via XAML. How would I go about using my canvas in a template?
When you create a type such as canvas as a resource, then you are creating ONE instance of the type. This means that you cannot place that resource in more than one location in your application (an element can only be in one place at a time). You should look at using control templates, I think.
You don't need any code behind for this.
Something like this:
<ControlTemplate x:Key="Icon">
<Canvas>
<Path ... />
<Path ... />
</Canvas>
</ControlTemplate>
Then elsewhere you do something like this:
<Button>
<Control Template="{StaticResource Icon}" />
</Button>
This constructs a regular looking button with your icon as it's content. The content of that button is your icon.
If, however, you want to completely redefine the template of your button, then you would do so
<ControlTemplate x:Key="Icon" TargetType="Button">
<Canvas>
<Path ... />
<Path ... />
</Canvas>
</ControlTemplate>
Then elsewhere you do something like this:
<Button Template="{StaticResource Icon}" />
Note that this isn't a great style for a button. Look at this example from Microsoft for an example of a more fully featured button template.
EDIT
Unless you have a ContentPresenter in your ControlTemplate, then there's no need to assign the template to a content control. Note that any class derived from Control can be templated, including Control itself. So in order to place an item into your view, then you can just use:
<Control Template="{StaticResource Icon}" />
This uses the widest applicable type in the hierarchy, which is also the lightest.
A good way to do define an icon for a button is to use a DrawingBrush and set it as the fill of a Rectangle that you embed in the Button:
<Button>
<Rectangle
Width="32"
Height="32"
Fill={Background myDrawingBrush}
/>
</Button>
myDrawingBrush must be defined in resources like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options">
<DrawingBrush x:Key="dialogerror" Stretch="Uniform">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing>
... define geometry here ...
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</ResourceDictionary>

xaml How reference a dynamic resource not as a attribute but as an element

I have an image I need to use in my application in several places. I want to define the image just once in a resource dictionary, and have my other xaml files just that definition. I can The one thing I haven't bee able to figure out is how to reference something defined as a xaml element instead of a attributed inside of a xaml attribute.
Here is my ResourceDictionary
# resourceDictionary.xaml
<LinearGradientBrush x:Key="MyGradient" StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="#A5000000" Offset="0"/>
<GradientStop Color="#00000000" Offset="1"/>
</LinearGradientBrush>
<Image x:Key="MyImage" Source="MyGlyph.png" Width="20" Height="20" />
So in my xaml I know how to reference the gradient as an attribute of a control object
<TextBlock Text="Sample Text" Background="{DynamicResource MessageGradient}"/>
But what I want to figure out his how to reference the Image which is a full blown control object. This example just creates a button that has the text "{DynamicResource MyImage}" in the button instead of the image.
<!-- Want MyImage to be content of the button -->
<Button>
{DynamicResource MyImage}
</Button>
Is there a simple way to do this, or will have I have to create a control template that contains just my image, and then in my xaml have a image tag that uses the control template?
Since you want the image to be the content of the button, you should be able to bind that to the Content property:
<Button Content="{DynamicResource MyImage}" />
<!-- Want MyImage to be content of the button -->
<Button>
<DynamicResource ResourceKey="MyImage"/>
</Button>
If you are trying to put an Image as Background of any control
<Button Content="My Button">
<Button.Background>
<ImageBrush ImageSource="MyGlyph.png"/>
</Button.Background>
</Button>

Resources