Using a XAML image as a button content - wpf

I am a beginner with Visual Studio.
I want to have separately defineable icons in my buttons. I want to achieve this using only XAML in order to keep my GUI stuff as separate as possible.
I'd like to be able to use it like this:
<Button x:Name="CallButton" Height="128px" Width="128px"
Style="{DynamicResource RoundButton}" Content="{StaticResource PhoneIcon}">
I have defined RoundButton and PhoneIcon in their respective Resource Dictionaries.
RoundButton:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="RoundButton" TargetType="{x:Type Button}">
<Setter Property="Content" Value="{Binding Grid}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="100" BorderThickness="2" x:Name="border"
BorderBrush="{TemplateBinding BorderBrush}">
<Grid>
<ContentPresenter VerticalAlignment="Center"
HorizontalAlignment="Center"
x:Name="contentPresenter" Opacity="1" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
PhoneIcon:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas x:Key="PhoneIcon">
<Path Stroke="Gray" Data="m 492.438 397.75 -2.375 -7.156 c -5.625 -16.719 -24.063 -34.156 -41 -38.75 l -62.688 -17.125 c -17 -4.625 -41.25 1.594 -53.688 14.031 L 310 371.438 C 227.547 349.157 162.891 284.5 140.641 202.063 l 22.688 -22.688 c 12.438 -12.438 18.656 -36.656 14.031 -53.656 L 160.266 63 C 155.641 46.031 138.172 27.594 121.485 22.031 l -7.156 -2.406 c -16.719 -5.563 -40.563 0.063 -53 12.5 L 27.391 66.094 c -6.063 6.031 -9.938 23.281 -9.938 23.344 -1.187 107.75 41.063 211.562 117.281 287.781 76.031 76.031 179.453 118.219 286.891 117.313 0.563 0 18.313 -3.813 24.375 -9.844 l 33.938 -33.938 c 12.437 -12.437 18.062 -36.281 12.5 -53 z" />
</Canvas>
</ResourceDictionary>
I have merged my Resource Dictionaries in the App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="RoundButton.xaml" />
<ResourceDictionary Source="Icons.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
My problem is that while I get the image displayed, it's disproportionately big. I have tried pretty many solutions so I'm starting to lose track of the tried with my trial-and-error approach.
How to get the image show right in the center of the button with correct size?
NB I have trimmed some of the code I'm posting here - but I have also tested the code I'm posting here and the problem persists.
Thanks in advance!

You may use a Grid instead of a Canvas in your PhoneIcon resource, and set the Path's Stretch property. This would scale the Path to a Size that fits the Button's bounds:
<Grid x:Key="PhoneIcon">
<Path Stretch="Uniform" ... />
</Grid>
You may even set the Grid size explicitly:
<Grid x:Key="PhoneIcon" Width="80">
<Path Stretch="Uniform" ... />
</Grid>
An even simpler solution would be to use the Path without any container:
<Path x:Key="PhoneIcon" Width="80" Stretch="Uniform" ... />

Related

Different style in corner radius between win 7 and win 10

I have a styled MenuItem in XAML and it looks different between Windows 7 and Windows 10.
MenuItem code:
<MenuItem Click="LangMouseLeftButtonUp" ContextMenuOpening="LangMouseRightButtonUp"
x:Name="LangImageIcon">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Language.ActualLanguage}" Value="EN">
<Setter Property="Icon">
<Setter.Value>
<Border CornerRadius="20" Width="26" Height="26"
Margin="0" Padding="0,0,0,0" Background="Transparent"
BorderBrush="White" BorderThickness="1">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White" Background="Transparent"
Text="EN" />
</Border>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Language.ActualLanguage}" Value="DE">
<Setter Property="Icon">
<Setter.Value>
<Border CornerRadius="20" Width="26" Height="26"
Margin="0" Padding="0,0,0,0" Background="Transparent"
BorderBrush="White" BorderThickness="1">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White" Background="Transparent"
Text="DE" />
</Border>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
How it looks in Windows 7
How it looks in Windows 10
How can I change the behavior from Windows 10 so that it looks like Windows 7. Or, how do I have to change the style so that Windows 7 shows the same button when I change the code so that it looks good in Windows 10?
That's because Windows 7 and Windows 10 uses different theme. Which in-turn defines the Icon sizes for the MenuItem. Ways in which you can solve this.
Try setting lesser Height and Width for your Border; If that works for you. Somewhere around 18 will work i guess.
Add below ResourceDictionary to your application / MenuItem's Resources. This tells your WPF application to use Aero theme.
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/PresentationFramework.Aero;V3.0.0.0;31bf3856ad364e35;component\themes/aero.normalcolor.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
If you're not specifying the version and public key token, you’ll have to copy the theme assembly to the executable folder.
You can define Template to your MenuItem which avoids using Icon and you can get the same working for both the operating system versions.

How to resolve "..dictionary entry must have associated key" error?

I've added a namespace to my App.xaml file in order to resolve my ViewModelLocator.cs location in the project.Then referenced the ns from a ResourceDictionary. But I get two errros when I add these:
..Each dictionary entry must have an associated key.
'ViewModelLocator' does not exist in XML namespace 'clr-namespace:MongoDBApp.ViewModels;assembly=MongoDBApp'
I've checked firstly that the namespace is correct for the location of the ViewModelLocator, which is: namespace MongoDBApp.ViewModels.
I also checked the syntax on the reference in the ResourceDictionary which seems correct. This solution didn't resolve the error and I've cleaned and rebuilt the solution a few times.
Can anyone advise on how to resolve this error?
The definition of the App.xml file is as follows, the ResourceDictionary is near the bottom of the file:
<Application x:Class="MongoDBApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:MongoDBApp.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MongoDBApp.ViewModels;assembly=MongoDBApp"
xmlns:validators="clr-namespace:MongoDBApp.Validator"
StartupUri="pack://application:,,,/Views/MainView.xaml"
d1p1:Ignorable="d">
<Application.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<Grid Width="16"
Height="16"
Margin="3 0 0 0"
VerticalAlignment="Center"
DockPanel.Dock="Right">
<Ellipse Width="16"
Height="16"
Fill="Red" />
<Ellipse Width="3"
Height="8"
Margin="0 2 0 0"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Fill="White" />
<Ellipse Width="2"
Height="2"
Margin="0 0 0 2"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Fill="White" />
</Grid>
<Border BorderBrush="Red"
BorderThickness="2"
CornerRadius="2">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
<ResourceDictionary>
<local:ViewModelLocator x:Key="mainViewModelLocator" ></local:ViewModelLocator>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Brown.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Brown.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Something like this should work, please note my ViewModelLocator in this case comes from prism (this is why I need IView, you don't if you use something else).
Base class
public class MyFormUserControl : UserControl, IView
{
public MyFormUserControl()
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
SetValue(ViewModelLocator.AutoWireViewModelProperty, true);
}
}
}
UserControl
<controls:MyFormUserControl x:Class="MyWpf1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="path to the base class">
your usual xaml goes here
</controls:MyFormUserControl>
Code behind
public partial class UserControl1: MyFormUserControl
{
public CrateFormView() : base()
{
InitializeComponent();
}
}

My Two DataGrid XAML Resources are Mutually Exclusive --- Why?

In order to have a nicer look, I am trying to add to my DataGrid 2 features that I found in separate places, but for some reason they just can't get along.
I can either have the section inside the blue rectangle or the one in the red one.
TIA
Put the <ControlTemplate> tag inside the <ResourceDictionary> tag, underneath the closing </ResourceDictionary.MergedDictionaries> tag.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes\DataGrid.Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<ControlTemplate x:Key="SelectAllButtonTemplate" TargetType="{x:Type Button}">
<Grid>
<Rectangle x:Name="Border" Fill="LightBlue" SnapsToDevicePixels="True" />
</Grid>
</ControlTemplate>
</ResourceDictionary>
</UserControl.Resources>

Vector icon as button content inside style not display in design time

I want to use vector icon as content inside Button style to use in multiple buttons (more than 20). So, I did like this:
my icon:
<Rectangle x:Key="DefaultsIcon" Height="20" Width="20" x:Shared="False" HorizontalAlignment="Center" VerticalAlignment="Center" Fill="{DynamicResource BlackBrush}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill">
<VisualBrush.Visual>
<Canvas Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="29.26" Height="32.5111" Canvas.Left="24.9269" Canvas.Top="21.3222" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="F1 M 24.9269,53.8333L 24.9269,21.3222L 36.6106,21.3222C 48.3282,21.3222 54.1869,26.6053 54.1869,37.1714C 54.1869,42.2319 52.588,46.274 49.3901,49.2977C 46.1922,52.3215 41.9324,53.8333 36.6106,53.8333L 24.9269,53.8333 Z M 32.3581,27.36L 32.3581,47.7956L 36.0156,47.7956C 39.2231,47.7956 41.7377,46.8509 43.5591,44.9617C 45.3806,43.0725 46.2914,40.5023 46.2914,37.2512C 46.2914,34.1791 45.3879,31.7625 43.5809,30.0015C 41.7739,28.2405 39.2376,27.36 35.972,27.36L 32.3581,27.36 Z "/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.OpacityMask>
</Rectangle>
my style:
<Style TargetType="Button" BasedOn="{StaticResource CameraButtonBaseStyle}" x:Key="CameraDefaultsButtonStyle">
<Setter Property="Margin" Value="{Binding Source={StaticResource CameraUISettings}, Path=OptionLBMargin, Mode=OneWay, Converter={uiConverters:DoubleToLeftMarginConverter}}"></Setter>
<Setter Property="ToolTip" Value="{Binding Source={StaticResource CameraLocalization}, Path=ToolTips.Default, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"></Setter>
<Setter Property="Content" Value="{DynamicResource DefaultsIcon}"></Setter>
</Style>
and apply style:
<Button Style="{StaticResource CameraDefaultsButtonStyle}"
Click="LoadDefaultX_OnClick">
</Button>
But the problem here is that in design time only 1 icon of all displaying and in runtime all icons displaying good. I want that in design time icons also were visible.
How to reach this?
The problem is DefaultsIcon style contains a visual. The style is created only once per lifetime of your app and reused as often as needed. But a visual can only be used once in the visual tree. A quick fix is to add x:Shared="Falsed" to DefaultsIcon. From the architectural perspective that's not the best solution because now for every usage of DefaultsIcon a new instance is created.
Because I want to share some code I add a new answer. My question is do you need the DefaultsIcon style somewhere else? If not you could do the following to get the same result:
<Style TargetType="Button" x:Key="CameraDefaultsButtonStyle" x:Shared="False" >
<Setter Property="Margin" Value="10"></Setter>
<Setter Property="ToolTip" Value="hello"></Setter>
<Setter Property="Content">
<Setter.Value>
<Path Width="29.26" Height="32.5111" Canvas.Left="24.9269" Canvas.Top="21.3222" Stretch="Fill" Fill="Black" Data="F1 M 24.9269,53.8333L 24.9269,21.3222L 36.6106,21.3222C 48.3282,21.3222 54.1869,26.6053 54.1869,37.1714C 54.1869,42.2319 52.588,46.274 49.3901,49.2977C 46.1922,52.3215 41.9324,53.8333 36.6106,53.8333L 24.9269,53.8333 Z M 32.3581,27.36L 32.3581,47.7956L 36.0156,47.7956C 39.2231,47.7956 41.7377,46.8509 43.5591,44.9617C 45.3806,43.0725 46.2914,40.5023 46.2914,37.2512C 46.2914,34.1791 45.3879,31.7625 43.5809,30.0015C 41.7739,28.2405 39.2376,27.36 35.972,27.36L 32.3581,27.36 Z "/>
</Setter.Value>
</Setter>
</Style>
The need to set x:Shared="False" is for me a sign to think of a small user control instead of a style.
No, I mean a mini user control as a replacement for your CameraDefaultsButtonStyle style, not only the Content within that style. The control could look like this:
<Button x:Class="TabControl1.CameraDefaultsButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
ToolTip="Hello">
<Grid>
<Path Width="29.26" Height="32.5111" Stretch="Fill" Fill="Black" Data="F1 M 24.9269,53.8333L 24.9269,21.3222L 36.6106,21.3222C 48.3282,21.3222 54.1869,26.6053 54.1869,37.1714C 54.1869,42.2319 52.588,46.274 49.3901,49.2977C 46.1922,52.3215 41.9324,53.8333 36.6106,53.8333L 24.9269,53.8333 Z M 32.3581,27.36L 32.3581,47.7956L 36.0156,47.7956C 39.2231,47.7956 41.7377,46.8509 43.5591,44.9617C 45.3806,43.0725 46.2914,40.5023 46.2914,37.2512C 46.2914,34.1791 45.3879,31.7625 43.5809,30.0015C 41.7739,28.2405 39.2376,27.36 35.972,27.36L 32.3581,27.36 Z "/>
</Grid>
</Button>
using System.Windows.Controls;
namespace TabControl1
{
/// <summary>
/// Interaction logic for CameraDefaultsButton.xaml
/// </summary>
public partial class CameraDefaultsButton : Button
{
public CameraDefaultsButton ()
{
InitializeComponent ();
}
}
}
You use it this way like the standard button:
<Window x:Class="TabControl1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:TabControl1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CameraDefaultsButton Margin="301,164,0,0"
HorizontalAlignment="Left"
Width="139"
Height="55"
VerticalAlignment="Top"/>
<local:CameraDefaultsButton Margin="301,236,0,0"
HorizontalAlignment="Left"
Width="139"
Height="55"
VerticalAlignment="Top"/>
</Grid>
</Window>
but don't forget to add the namespace where you use it:
xmlns:local="clr-namespace:TabControl1"

Reuseable help Button and help popup

I have about 30 views and corresponding VMs in my application. I have a HelpButton next to most user controls which gives some description of what the field is for. When the user clicks on that Button, a Popup opens and gives the description. What will be the best way to make this functionality reusable in all the views?
I have created a ControlTemplate for the HelpButton and the help Popup but can I go further than this? Thanks.
<Style x:Key="HelpButton" TargetType="{x:Type Button}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Ellipse Focusable="True" Height="16" Width="16">
<Ellipse.Fill>
<ImageBrush ImageSource="../../Resources/Icons/Help.png" />
</Ellipse.Fill>
</Ellipse>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I see that it's been a while since this question was asked, but I figured I would reply in case someone else comes across this problem and is looking for a solution.
Put it in a ResourceDictionary in a separate Resources.xaml that is accessible from all of your other views:
<ResourceDictionary xmlns...>
<Style x:Key="HelpButton" TargetType="{x:Type Button}" >
...
</Style>
</ResourceDictionary>
Then use it from your Resources section (as you no doubt did before) in your xaml like this:
<UserControl xmlns...>
<UserControl.Resources>
<ResourceDictionary Source="Resources.xaml"/>
</UserControl.Resources>
<Button Style="{StaticResource HelpButton}" />
</UserControl>

Resources