WPF - Adding a ObjectDataProvider breaks my designer - c#

Perhaps i'm not using the ObjectDataProvider correctly, but im following the MSDN examples so im not sure whats going wrong.
Goal: When i click a button, it will close the window by calling a method "exitButtonMethod" which simple does this.Close();.
<Window x:Class="WpfApplication1.Window1"
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:WpfApplication1"
Title="Window1" WindowStyle="None" AllowsTransparency="True"
WindowStartupLocation="CenterScreen" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="254" Width="438" Opacity="1" Background="{x:Null}">
<Window.Resources>
<ObjectDataProvider ObjectType="{x:Type local:Window1}"
MethodName="exitButtonMethod" x:Key="callExitButtonMethod">
</ObjectDataProvider>
<Style x:Key="ExitButtons" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="exitButtonTemplate" TargetType="Button">
<Grid>
<Ellipse x:Name="exitButtonEllipse" Fill="#597E0000"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="exitButtonEllipse" Property="Fill" Value="#897E0000" />
<Binding Source="{StaticResource callExitButtonMethod}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Width="400" Height="200" Opacity="1">
<Rectangle Height="200" HorizontalAlignment="Left" VerticalAlignment="Top" Name="rectangle1" Stroke="#FF7E0000" Width="400" RadiusX="40" RadiusY="40" Fill="#64860000" StrokeThickness="3" />
<Button Style="{StaticResource ExitButtons}" Content="X" Height="25" Width="25" Margin="359,16,16,0" VerticalAlignment="Top" Focusable="True" FontSize="15" FontFamily="PMingLiU" Foreground="#FF7E0000" Opacity="1"/>
</Grid>
The error is that it simply breaks my designer and gives me the following error in the designer:
System.Runtime.Remoting.RemotingException
[228940] Designer process terminated unexpectedly!

What the purpose of ObjectDataProvider is to create objects in XAML that you can bind to. You can also use it to bind to the results of calling a method on that object, which is what you are trying to do.
Here you are making an new object with the type of Window1 and binding to the method callExitButtonMethod. So you are unintentionally making a new window inside your window.
<ObjectDataProvider ObjectType="{x:Type local:Window1}"
MethodName="exitButtonMethod" x:Key="callExitButtonMethod">
</ObjectDataProvider>
Now that new window, when created, also has a window inside of it... etc. and you get an infinite loop of making windows.
This is why you are getting a stack overflow.
What you are trying to do is much simpler than what you are currently doing. In order to call a method on a button when you click it, you can simply do:
<Button Click="NameOfMethodHere" />
In your case, just add the Click parameter to your button and get rid of the ObjectDataProvider.
EDIT: Also to set events from a Style, see EventSetter.

Related

How can I avoid overriding a window-wide style in WPF?

I'm using the MaterialDesign nuget package for my WPF application.
According to the tutorial, by applying window-wide properties, every element will inherit the MaterialDesign style.
However, if I apply a custom style to an element, that element loses its MaterialDesign style.
I can get around this by applying inline styles, but that is very repetitive and error prone.
I think the picture shows it better:
And here is my xaml:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestApp"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" x:Class="TestApp.MainWindow"
mc:Ignorable="d"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
TextElement.FontWeight="Regular"
TextElement.FontSize="13"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="{DynamicResource MaterialDesignFont}"
Title="MainWindow" Height="450" Width="600">
<!--All of the above is meant to apply Material Design to the entire Window-->
<StackPanel>
<StackPanel.Resources>
<!--This style overrides the Material Design style entirely,
instead of just Margin and Horizontal Alignment-->
<Style x:Key="SpacedButton" TargetType="Button">
<Setter Property="Margin" Value="0 10"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</StackPanel.Resources>
<!--Material Design only works by applying properties directly to elements-->
<Button Content="Button #1" HorizontalAlignment="Center" Margin="0 10"/>
<Button Content="Button #2" Style="{DynamicResource SpacedButton}"/>
<Button Content="Button #3" Style="{DynamicResource SpacedButton}"/>
<Button Content="Button #4" HorizontalAlignment="Center" Margin="0 10"/>
</StackPanel>
</Window>
As you can see, only elements with inline properties keep the MaterialDesign style, but by applying a custom style, the MaterialDesign style is lost.
How can I make sure that MaterialDesign is applied to every element, while still being able to override specific properties with custom styles?
Sorry if some of the terminology is wrong, I'm pretty new to WPF.
Use Style.BasedOn to inherit properties from whichever other style is in scope:
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
...etc....
Or a specific one:
<Style x:Key="ThisStyleKey" TargetType="{x:Type Button}" BasedOn="{StaticResource OtherStyleKey}">
...etc....

Force transparent background on custom UserControl

I want to create a new UserControl and reroute the Background property to use it elsewhere than in the UserControl.Background property (like it is done on the checkbox for example).
Here is a simple custom usercontrol:
<UserControl 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"
xmlns:local="clr-namespace:Controls"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
x:Class="Controls.HexagonalTile"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="300">
<Grid>
<ed:RegularPolygon Fill="{Binding ElementName=LayoutRoot, Path=Background}" StrokeThickness="5" Stroke="Black"/>
</Grid>
And I want to use it like this:
<Controls:HexagonalTile HorizontalAlignment="Left" Height="100" Width="100" Background="Aqua" />
But when I do this, the corner of my user control, outside of the hexagone, take the background color too. I want them to stay transparent.
Thank's for your help.
The reason why this is happening is because the default ControlTemplate for a UserControl has a Border with a TemplateBinding to the Background property.
However, you can re-template the control like this to achieve your goal:
<UserControl x:Class="WpfApp4.HexagonalTile"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing">
<UserControl.Template>
<ControlTemplate TargetType="{x:Type UserControl}">
<Grid>
<ContentPresenter />
</Grid>
</ControlTemplate>
</UserControl.Template>
<Grid>
<ed:RegularPolygon
Fill="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Stroke="Black"
StrokeThickness="5" />
</Grid>
</UserControl>
I hope this helps!

WPF Custom Toggle button with ContentControl

It seems very hard to achieve something rather trivial in WPF...
I need to design a toggle button with a specific look (and feel). I made a small project to demonstrate the problem.
"ToggleButton user control" :
<UserControl x:Class="WpfApp4.UserControl1"
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="450" d:DesignWidth="800"
Name="Bla">
<UserControl.Resources>
<Style TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ToggleButton
Width="300" Height="300">
<ContentControl Content="{Binding ElementName=Bla, Path=MainContent}"/>
</ToggleButton>
</UserControl>
Dependency property:
public static DependencyProperty MainContentProperty = DependencyProperty.Register(
"MainContent",
typeof(object),
typeof(UserControl1),
null);
public object MainContent
{
get => GetValue(MainContentProperty);
set => SetValue(MainContentProperty, value);
}
The way I want to use the control:
<local:UserControl1>
<TextBlock>Whatever</TextBlock>
</local:UserControl1>
When I run the program, the textbox appears "Whatever", but the style is not applied, the ellipse won't show.
What's the correct way of doing this?
=== Update ===
OK, getting somewhere... finally...
Now I got this as user control:
<UserControl x:Class="WpfApp4.UserControl1"
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"
xmlns:wpfApp4="clr-namespace:WpfApp4"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"
Name="Bla">
<UserControl.Resources>
<Style TargetType="wpfApp4:UserControl1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton>
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter Content="{Binding ElementName=Bla, Path=MainContent}" />
</Grid>
</ToggleButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ContentPresenter/>
</UserControl>
And this is how I use it:
<local:UserControl1>
<local:UserControl1.MainContent>
<TextBlock>Whatever</TextBlock>
</local:UserControl1.MainContent>
</local:UserControl1>
That finally gives me a toggle button with the style applied (the ellipse shows up) and the textbox is shown as well.
So, this works. Is this the way you mean it should work? Or can it be simplified?
It should be more like
<local:UserControl1>
<local:UserControl1.MainContent>
<TextBlock>Whatever</TextBlock>
</local:UserControl1.MainContent>
</local:UserControl1>
But you should look forward overriding ContentControl which would be more adequate rather then using UserControl.
By the way why did you put a ContentControl inside ToggleButton? ToggleButton by itself is a ContentControl it has it's own Content property.
Update:
All depends on what you whant to do. If it is only change the visual of the toggle button, then just create a toggle button style like this:
<ToggleButton>
<TextBlock>Whatever</TextBlock>
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
Ofcorse if you want to use your style across the application it is better to define the style in a resource dictionnary (for exemple in App.xaml), give it a key and call it on each toggle button using {StaticResource key}.
If on the other hand, you want to add some logic, you have to create a control class inheriting from ToggleButton and add the logic inside.

WPF Window Blur + Drop shadow

I've done some searching but I can't seem to find a way to use both a drop shadow on my window, and have the window's background blurred.
I'm currently using https://github.com/riverar/sample-win32-acrylicblur (all blur code in MainWindow.xaml.cs) to blur the background, but since the dropshadow requires some padding in the window to render the dropshadow in, the space of the dropshadow gets the blur applied to it too.
I tried using an OpacityMask, but that didn't seem to help. In fact, even when setting the Window's Opacity property to 0, the blur still showed, so I fear that it's not possible with this method of blurring.
One of the packages I already am using is Microsoft.Windows.Shell, which I need to rebuild the default buttons I lose after applying the drop shadow, perhaps this has something helpful.
TLDR:
Is there a way to use an Aero-style blurred Window and a drop shadow together? Ideally without installing extra packages, but if there's no other way I'll have to bite the bullet.
I'm on the latest versions of .Net etc. as of 03/08/2018
Do you mean the effect shown below?
If so, you can write the XAML code to get it.
<Window x:Class="Walterlv.Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"
AllowsTransparency="True" WindowStyle="None" Background="{x:Null}">
<Grid>
<Rectangle x:Name="ShadowShape" Fill="White" Margin="8">
<Rectangle.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" />
</Rectangle.Effect>
</Rectangle>
<Border x:Name="BackgroundBorder" Margin="8" ClipToBounds="True">
<Rectangle x:Name="BlurringShape" Margin="-32">
<Rectangle.Fill>
<ImageBrush ImageSource="Sample.jpg"/>
</Rectangle.Fill>
<Rectangle.Effect>
<BlurEffect KernelType="Gaussian" Radius="32" />
</Rectangle.Effect>
</Rectangle>
<Border.CacheMode>
<BitmapCache />
</Border.CacheMode>
</Border>
</Grid>
<Grid>
<!-- Write your own content here... -->
</Grid>
</Window>
Notes:
I write three UIElement to implement that effect:
The BlurringShape renders a bitmap and blur itself. It blurs at the radius 32, so it should set a -32 margin to avoid the transparent blur.
The BackgroundBorder clips the BlurringShape so that the blur will not spill over.
Because we have clipped the BackgroundBorder, so if we set a DropShadowEffect on it, it will be clipped. We should create another shape to render the DropShadowEffect. That is the ShadowShape.
If you want your style more reusable, you can take this code below:
<Window x:Class="Walterlv.Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Title="MainWindow" Height="450" Width="800">
<Window.Background>
<ImageBrush ImageSource="High+Sierra.jpg"/>
</Window.Background>
<Window.Style>
<Style TargetType="Window">
<Setter Property="AllowsTransparency" Value="True" />
<Setter Property="WindowStyle" Value="None" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<Grid>
<Rectangle Fill="White" Margin="8">
<Rectangle.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" />
</Rectangle.Effect>
</Rectangle>
<Border x:Name="BackgroundBorder" Margin="8" ClipToBounds="True">
<Rectangle Margin="-32" Fill="{TemplateBinding Background}">
<Rectangle.Effect>
<BlurEffect KernelType="Gaussian" Radius="32" />
</Rectangle.Effect>
</Rectangle>
<Border.CacheMode>
<BitmapCache />
</Border.CacheMode>
</Border>
<ContentPresenter Margin="8" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Style>
<Grid>
<!-- Write your own content here ... -->
</Grid>
</Window>

How do I assign an EventTrigger to a reusable Button in its parent?

My reusable Button is basically a single button, of which ControlTemplate consists of a TextBlock and an Image. The Text property of TextBlock binds to a DependencyProperty to be exposed; similarly, the Source property of Image binds to a DependencyProperty. Here is the code for this Button.
<Button x:Class="Core.Resource.UserControlResource.NavigationButton1"
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"
xmlns:local="clr-namespace:Core.Resource.UserControlResource"
mc:Ignorable="d"
x:Name="myself">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="63"></ColumnDefinition>
<ColumnDefinition Width="63"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Name="IconImage" Height="42" Width="42" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Source="{Binding ElementName=myself, Path=ScreenIcon}" />
<TextBlock Grid.Column="1" Text="{Binding ElementName=myself, Path=ScreenTitle}" FontSize="25" TextWrapping="Wrap" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
where ScreenTitle and ScreenIcon are the aforementioned DependecyProperty.
Now, I want to use this Button in its "parent", a UserControl. The code will be like
<UserControl x:Class="Core.ParentControl"
x:Name="parent"
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:usrCtrlResrc="clr-namespace:Core.Resource.UserControlResource;assembly=Core.Resource"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32"/>
<ColumnDefinition Width="32"/>
<Grid.ColumnDefinitions>
<usrCtrlResrc:NavigationButton1 ScreenTitle="sample Screen Title" ScreenIcon="/Core.Resource;component/MediaResource/pencilEdit.png">
<TextBlock Grid.Column="1" Name="txtBlk" Text="SampleSample"/>
</Grid>
However, in order to add reactions when the Button is clicked (say to change the Grid.ColumnSpan property of TextBlock "txtBlk" to 2), what I want to do else is assign EventTriggers to my reusable Button in the "parent". I initially thought of two ways, but none of them works.
In my reusable Button, bind Style.Triggers to a DependencyProperty to get exposed to its "parent". However, it pops up "The property Triggers does not have an accessible setter".
Move the Style of my reusable Button to a ResourceDictionary and assign a Key for the "parent" to use. However, by doing this, I am not sure how to handle my two DependencyProperty, because it is not supposed to have code-behind for a ResourceDictionary file.
Any other workarounds? Thanks in advance.
I finally resolve this problem by directly override its Triggers. Code is as below.
<usrCtrlResrc:NavigationButton1 ScreenTitle=... ScreenIcon=...>
<usrCtrlResrc:NavigationButton1.Triggers>
<EventTrigger RoutedEvent="usrCtrlResrc:NavigationButton1.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
...
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</usrCtrlResrc:NavigationButton1.Triggers>
</usrCtrlResrc:NavigationButton1>

Categories