WPF Override style in different assembly - c#

I am trying to make a plugin WPF application that can be referenced on other applications and the latter can change the styles of the former.
My example is based on Xceed's BusyIndicator. I have a style for the BusyIndicator in my plugin WPF app and want the style of that BusyIndicator to be changed on other applications.
Example:
WPF Plugin Application: Let's call it OverrideBusyIndicator. The solution looks like the image below where a MainWindow containing the BusyIndicator exists and the BusyIndicator style is in BusyContextResourceDictionary.xaml
The content of BusyContextResourceDictionary.xaml is this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xceed="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type xceed:BusyIndicator}">
<Setter Property="BusyContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="Downloading Email" FontWeight="Bold" HorizontalAlignment="Center"/>
<StackPanel Margin="4">
<TextBlock Text="Downloading message 4/10..."/>
<ProgressBar Value="40" Height="15"/>
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="Pause" HorizontalAlignment="Right" Margin="0 0 2 0"/>
<Button Grid.Column="1" Content="Cancel" HorizontalAlignment="Left" Margin="2 0 0 0"/>
</Grid>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
I created another solution that will reference my assembly above. Let's call it OverrideBusyIndicator2. This one has no main window and its App.xaml just calls the other OverrideBusyIndicator.MainWindow. I then added a BusyContextResourceDictionary2.xaml that I EXPECT to override the style of the BusyIndicator but it does not. Any way I could achieve this behavior?
<Application x:Class="OverrideBusyIndicator2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xceed="http://schemas.xceed.com/wpf/xaml/toolkit"
StartupUri="pack://application:,,,/OverrideBusyIndicator;component/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="BusyContextResourceDictionary2.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
For sample purposes BusyContextResourceDictionary2.xaml will just change the textblock text from "Downloading email" to "Not Downloading email".
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xceed="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/OverrideBusyIndicator;component/BusyContextResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!--I am using BasedOn to override the BusyContextResourceDictionary.xaml-->
<Style TargetType="{x:Type xceed:BusyIndicator}" BasedOn="{StaticResource {x:Type xceed:BusyIndicator}}">
<Setter Property="BusyContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="NOT Downloading Email" FontWeight="Bold" HorizontalAlignment="Center"/>**

It would be helpful to see the style for the xceed:BusyIndicator control. You might need to define the default style key property for your control.

Related

How can I convert an `enum` to a XAML vector icon object and display it multiple times within the same window?

I have a resource dictionary in a Resources.xaml file containing multiple vector icons (XAML format, Canvas in a Viewbox):
<ResourceDictionary>
<Viewbox x:Key="Icon1" x:Shared="False">
...
</Viewbox>
<Viewbox x:Key="Icon2" x:Shared="False">
...
</Viewbox>
</ResourceDictionary>
These icons can be displayed in a WPF window multiple times because I have used the x:Shared="False setting. For example, ...
<ContentControl Content="{StaticResource Icon1}" />
<ContentControl Content="{StaticResource Icon1}" />
... displays the Icon1 icon twice as expected.
Now I'd like to convert an enum to the icon object so that an icon can be displayed based on an enum value (for nodes in a tree view). You would usually declare an EnumToObjectConverter in the Resources.xaml:
<local:EnumToObjectConverter x:Key="TreeIcons">
<ResourceDictionary>
<Viewbox x:Key="Icon1" x:Shared="False">
...
</Viewbox>
<Viewbox x:Key="Icon2" x:Shared="False">
...
</Viewbox>
<ResourceDictionary>
</local:EnumToObjectConverter>
But since this is an embedded resource dictionary the x:Shared setting does not have any effect (https://learn.microsoft.com/en-us/dotnet/framework/xaml-services/x-shared-attribute) and referencing the image through the converter results in the icon being displayed only once in the Window or tree view, even when referenced in multiple places (the other places remain blank).
How can I do a mapping from an enum to the vector icon object so that icons are still properly displayed in multiple places?
Update: This example demonstrates the effect of the x:Shared setting (this is a NET Core 3.0 WPF application in case it makes any difference).
MainWindow.xaml
<Window x:Class="XamlIconTest.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"
xmlns:local="clr-namespace:XamlIconTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<StackPanel>
<Label Content="Icon1 (1st)" />
<ContentControl Content="{StaticResource Icon1}" Margin="8"/>
<Separator />
<Label Content="Icon1 (2nd)" />
<ContentControl Content="{StaticResource Icon1}" Margin="8"/>
<Separator />
<Label Content="Icon2 (1st)" />
<ContentControl Content="{StaticResource Icon2}" Margin="8"/>
<Separator />
<Label Content="Icon2 (2nd)" />
<ContentControl Content="{StaticResource Icon2}" Margin="8"/>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace XamlIconTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Resources.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XamlIconTest">
<!-- Icon1 without x:Shared -->
<Path x:Key="Icon1"
Width="37.9858" Height="46.6386" Canvas.Left="19.186" Canvas.Top="14.2229" Stretch="Fill" Fill="#FF000000" Data="F1 M 38.1789,60.8614L 19.186,37.7428L 38.1686,14.2229L 57.1718,37.7531L 38.1789,60.8614 Z "/>
<!-- Icon2 with x:Shared -->
<Path x:Key="Icon2" x:Shared="False"
Width="37.9858" Height="46.6386" Canvas.Left="19.186" Canvas.Top="14.2229" Stretch="Fill" Fill="#FF000000" Data="F1 M 38.1789,60.8614L 19.186,37.7428L 38.1686,14.2229L 57.1718,37.7531L 38.1789,60.8614 Z "/>
</ResourceDictionary>
Displayed main window (note the missing Icon1 in the first row):
Your question seems to boil down to two separate topics:
The primary one, which is how to share vector graphics in a context where x:Shared has no effect (i.e. in a resource dictionary that's defined as a child of your converter).
An implied secondary one, which is how to property select a specific vector graphic given an input value (e.g. an enum value).
First I will note: as a general rule it is my preference to use templates instead of x:Shared=false with explicit resources. It winds up doing basically the same thing — instantiating new visual objects for each value displayed — but IMHO is more idiomatic for WPF, which is designed entirely around the concept of templating and binding.
As far as addressing your issues goes…
Your MCVE does not involve code that uses a converter, but the basic principle would be the same, so I will provide an example based on the MCVE, not using a converter. The approach involves doing as I suggested in the comments, which is to declare a resource containing the path's data (i.e. the geometry), and then reuse that resource as needed. The data itself isn't a visual, and so can be shared arbitrarily.
First, the resource:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestSO58533019ShareVectorData"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<PathGeometry x:Key="IconGeometry1">F1 M 38.1789,60.8614L 19.186,37.7428L 38.1686,14.2229L 57.1718,37.7531L 38.1789,60.8614 Z</PathGeometry>
</ResourceDictionary>
Then to use that, you can just define a DataTemplate that maps a Geometry object to the visual you want (in this case, a Path object):
<Window x:Class="TestSO58533019ShareVectorData.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"
xmlns:local="clr-namespace:TestSO58533019ShareVectorData"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml" />
<ResourceDictionary>
<DataTemplate DataType="{x:Type Geometry}">
<Path Width="37.9858" Height="46.6386" Canvas.Left="19.186" Canvas.Top="14.2229"
Stretch="Fill" Fill="#FF000000"
Data="{Binding}"/>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<StackPanel>
<Label Content="IconGeometry1 (1st)" />
<ContentControl Content="{StaticResource IconGeometry1}" Margin="8"/>
<Separator />
<Label Content="IconGeometry1 (2nd)" />
<ContentControl Content="{StaticResource IconGeometry1}" Margin="8"/>
</StackPanel>
</Grid>
</Window>
This results in the display of the icon twice:
Now, the above approach could still be used with your converter technique. Your converter could return different Geometry objects depending on the enum value, which in turn could be bound to the Data property of a Path object as above. With some contortions, you could even have a Path resource item that does this, using x:Shared=false to reuse that resource item.
But IMHO that would be harder than necessary and not the right way to go. To me, conceptually what is going on is that you have an enum value, and you want to represent that very value with some graphic, depending on the value. That's exactly what WPF's templating features are for! They map one data type to another (i.e. your enum type to a Path object), and with styles you can conditionally configure the templated object as needed.
For the sake of simplicity I will use int rather than an actual enum value. But the basic idea is exactly the same. Note that a key benefit of doing it this way is to minimize the amount of code-behind. You declare for WPF what it is you want to happen, instead of having to write procedural code to do something yourself that WPF could instead do for you.
First, let's define a couple of different icons:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestSO58533019ShareVectorData"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<PathGeometry x:Key="IconGeometry1">F1 M 38.1789,60.8614L 19.186,37.7428L 38.1686,14.2229L 57.1718,37.7531L 38.1789,60.8614 Z</PathGeometry>
<PathGeometry x:Key="IconGeometry2">F1 M 38.1789,60.8614L 19.186,37.7428L 57.1718,37.7531L 38.1789,60.8614 Z</PathGeometry>
</ResourceDictionary>
Now, let's define a template for int, where that template uses style triggers to use the appropriate geometry data, and the bound value is simply that int value:
<Window x:Class="TestSO58533019ShareVectorData.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:p="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:TestSO58533019ShareVectorData"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml" />
<ResourceDictionary>
<DataTemplate DataType="{x:Type s:Int32}">
<Path Width="37.9858" Height="46.6386" Canvas.Left="19.186" Canvas.Top="14.2229"
Stretch="Fill" Fill="#FF000000">
<Path.Style>
<p:Style TargetType="Path">
<p:Style.Triggers>
<DataTrigger Binding="{Binding}" Value="1">
<DataTrigger.Setters>
<Setter Property="Data" Value="{StaticResource IconGeometry1}"/>
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="2">
<DataTrigger.Setters>
<Setter Property="Data" Value="{StaticResource IconGeometry2}"/>
</DataTrigger.Setters>
</DataTrigger>
</p:Style.Triggers>
</p:Style>
</Path.Style>
</Path>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<StackPanel>
<Label Content="1st int" />
<ContentControl Margin="8">
<ContentControl.Content>
<s:Int32>1</s:Int32>
</ContentControl.Content>
</ContentControl>
<Separator />
<Label Content="2nd int" />
<ContentControl Margin="8">
<ContentControl.Content>
<s:Int32>2</s:Int32>
</ContentControl.Content>
</ContentControl>
</StackPanel>
</Grid>
</Window>
With that code, you'll get this:

ResourceDictionary and Serious Performance Issue

I have two simple ResourceDictionary in my application.
Converters.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:l="clr-namespace:MyApp" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<l:BitmapToBitmapSourceConverter x:Key="BitmapToBitmapSourceConverter"/>
<l:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter"/>
</ResourceDictionary>
Styles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ComboBoxBase" TargetType="{x:Type ComboBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="16" Margin="0,2,2,2" Source="{Binding Image, Converter={StaticResource BitmapToBitmapSourceConverter}}" VerticalAlignment="Center" Width="16"/>
<TextBlock Text="{Binding}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
When they are merged inside my MainWindow.xaml:
<Window>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Properties/Converters.xaml"/>
<ResourceDictionary Source="Properties/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Canvas>
<ComboBox ItemsSource="{Binding Path=Items}" SelectedItem="{Binding Mode=TwoWay, Path=SelectedItem}" Style="{StaticResource ComboBoxBase}"/>
I get an exception when it's time to find BitmapToBitmapSourceConverter inside my Styles.xaml, because it has been defined inside another ResourceDictionary.
Someone suggested me to move my resources from MainWindow.xaml to App.xaml in order to avoid that kind of exceptions. I did it, and suddenly... drama! I don't get anymore that exception, but my application loading time has become 10 times longer. Sometimes it takes more than 5 seconds to show up on my screen, and normally the Window content is plain white for 1 or 2 seconds more.
I tried to run my application from the exe itself, I tried to run it in both Debug and Release mode... nothing. No way to get a good performance after moving my resource files and I can't deliver the application to my customer actually.
Anyone can explain me why and provide a good solution for this?!

The ControlTemplate is causing the following error "The property 'Content' is set more than once"

I'm just trying for the first time to use a ControlTemplate for a button that I want to create.
However, as soon as I put the tag <ControlTemplate> anywhere, it is coming up with an error.
<Window x:Class="MAQButtonTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="695" Width="996">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="#FFE9F1F6"></Grid>
<Grid Grid.Column="1" Background="#FFD2E3ED">
</Grid>
</Grid>
<ControlTemplate></ControlTemplate>
</Window>
Where do I put the tag so that this error doesn't come up?
Templates, like Styles, Brushes, DataTemplates are Resources, and usually placed inside a resource dictionary or a resource section in your control.
<Window>
<Window.Resources>
<ControlTemplate TargetType="{x:Type Button}"/>
<ControlTemplate x:Key="myTemplate" TargetType="{x:Type Button}"/>
<Window.Resources>
<!-- this will use your implicit defined template -->
<Button />
<!-- this will use your explicit defined template called myTemplate-->
<Button Template="{StaticResource myTemplate}"/>
</Window>

How to make a Style that only exists within the context of a ResourceDictionary

How can I create a style that only exists within the context of a ResourceDictionary, but not in the context of controls that include that ResourceDictionary?
For instance, I want to be able to have a ResourceDictionary that looks like this:
<!-- ControlTemplates.xaml -->
<ResourceDictionary>
<!-- Private Local styles used to set up the publicly usable templates -->
<Style x:Key="TextBoxes" TargetType="TextBox">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<!-- End of Private Local Stuff -->
<!-- Public Dictionary Resources Follow -->
<ControlTemplate x:Key="CustomTextBox">
<TextBox Style="{StaticResource TextBoxes}" />
</ControlTemplate>
</ResourceDictionary>
And then in some other control or window, I want to be able to go:
<Window>
<Window.Resources>
<ResourceDictionary Source="ControlTemplates.xaml">
</Window.Resources>
<Grid>
<!-- This Should Work -->
<CustomControl Template="{StaticResources CustomTextBox}">
<!-- This Should NOT Work! -->
<TextBox Template="{StaticResources TextBoxes}">
</Grid>
</Window>
One way to get quite close to what you are looking for is to move the "private" styles from ControlTemplates.xaml into their own ResourceDictionary, and then reference that resource dictionary from within the control templates in ControlTemplates.xaml:
ControlTemplates.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- By referencing the ResourceDictionary from within the ControlTemplate's
resources it will only be available for the ControlTemplate and not for those
who reference ControlTemplates.xaml -->
<ControlTemplate x:Key="CustomTextBox">
<ControlTemplate.Resources>
<ResourceDictionary Source="ControlTemplatePrivateStyles.xaml" />
</ControlTemplate.Resources>
<TextBox Style="{StaticResource TextBoxes}" Text="Some text" />
</ControlTemplate>
</ResourceDictionary>
ControlTemplatePrivateStyles.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="TextBoxes" TargetType="TextBox">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</ResourceDictionary>
Then the xaml for the window would look like this:
<Window x:Class="ResourceDictionaryPrivateStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ResourceDictionaryPrivateStyle="clr-namespace:ResourceDictionaryPrivateStyle"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary Source="ControlTemplates.xaml" />
</Window.Resources>
<StackPanel>
<!-- This works -->
<ResourceDictionaryPrivateStyle:CustomControl Template="{StaticResource CustomTextBox}" />
<!-- This does not work, unless you explicitly reference ControlTemplatesPrivateStyles.xaml here in the window-->
<TextBox Text="Text" Style="{StaticResource TextBoxes}" />
</StackPanel>
</Window>
This way you could not use the "private" styles unless you explicitly reference that resource dictionary. They will not be accessible by just referncing the ControlTemplates.xaml resource dictionary.

What WPF control or approach for this requirement?

Just being new to WPF I"m not sure what control or approach would be best for this requirement, for a WPF application.
I want to present a summary table of information, but the user should be able to decide to view the information based on either: "All Time", Month, Week or Day.
I'd like to visually have the selection of the option appear at the top of this section and have it appear as a TabControl
I'm not sure however whether TabControl is the best choice re repeating the table for each Tab Item
So overall functionally what would work is just radio buttons across the top, however what I want visually is a TabControl look
What would be the best way to achieve the TabControl look but with a programming approach for which I don't have to repeat things in each Tab Item?
For example, would I use a TabControl and then a WPF template to do the equivalent of an include in each Tab Item but with a different input parameter? (haven't used WPF templates before)
Thanks
Since you want the behavior of a group of RadioButtons and you want the visual appearance of a TabItem, you should use RadioButton controls and style them such that they look like TabItem controls. Here is a very simple example:
<Window x:Class="TabTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Border x:Name="tabBorder" BorderThickness="1" BorderBrush="Black"
Margin="0,0,-4,0"
CornerRadius="2,12,0,0"
Background="White"
SnapsToDevicePixels="True">
<ContentPresenter
Margin="12,2,12,2"
VerticalAlignment="Center"
HorizontalAlignment="Left"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Panel.ZIndex" Value="100" />
<Setter TargetName="tabBorder" Property="Background" Value="LightBlue" />
<Setter TargetName="tabBorder" Property="BorderThickness" Value="1,1,1,0" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,-1" Panel.ZIndex="1">
<RadioButton>All Time</RadioButton>
<RadioButton IsChecked="True">Month</RadioButton>
<RadioButton>Week</RadioButton>
<RadioButton>Day</RadioButton>
</StackPanel>
<Border Grid.Row="1" Background="LightBlue"
BorderThickness="1" BorderBrush="Black"
SnapsToDevicePixels="True">
<Button Margin="10" Grid.Row="1">This is a test</Button>
</Border>
</Grid>
</Window>
In this example, the Button is the place where you would put your summary table.
Greg, I suppose, grouping grid would be the most ideal control of your requirement. Either you can customize the datagrid as explained in the following article. But this would take more time to get things right.
http://blog.smoura.com/wpf-toolkit-datagrid-part-iv-templatecolumns-and-row-grouping/
or else you could make use of commercial WPF Grid grouping control which would match you requirement.
What would be the best way to achieve the TabControl look but with a programming approach for which I don't have to repeat things in each Tab Item?
Use a TabControl. Have each TabItem contain a CollectionViewSource based on the same underlying collection of data, but with a different filter. Use a DataTemplate to present the CollectionViewSource.
Filtering requires some kind of code-behind, but here's a XAML-only demo that does sorting:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">
<Page.Resources>
<XmlDataProvider x:Key="Data">
<x:XData>
<Data xmlns="">
<Item Date="2010-01-01" Value="January"/>
<Item Date="2010-02-01" Value="February"/>
<Item Date="2010-03-01" Value="March"/>
<Item Date="2010-04-01" Value="April"/>
<Item Date="2010-05-01" Value="May"/>
<Item Date="2010-06-01" Value="June"/>
<Item Date="2010-07-01" Value="July"/>
<Item Date="2010-08-01" Value="August"/>
<Item Date="2010-09-01" Value="September"/>
</Data>
</x:XData>
</XmlDataProvider>
<CollectionViewSource x:Key="ByDate" Source="{Binding Source={StaticResource Data}, XPath=Data/Item}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="#Date"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<CollectionViewSource x:Key="ByValue" Source="{Binding Source={StaticResource Data}, XPath=Data/Item}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="#Value"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<DataTemplate DataType="{x:Type CollectionViewSource}">
<Border Margin="5" BorderBrush="DodgerBlue" BorderThickness="1" CornerRadius="4">
<DockPanel Margin="5">
<Label DockPanel.Dock="Top">This is here to show how you can make the layout of your TabItems complex without repeating yourself.</Label>
<ListBox DockPanel.Dock="Top" x:Name="Items" ItemsSource="{Binding}" DisplayMemberPath="#Value" SelectedValuePath="#Value"/>
<DockPanel>
<Label>Selected item: </Label>
<Label Content="{Binding ElementName=Items, Path=SelectedValue}"/>
</DockPanel>
</DockPanel>
</Border>
</DataTemplate>
</Page.Resources>
<Grid>
<TabControl>
<TabItem Header="By date" Content="{StaticResource ByDate}"/>
<TabItem Header="By value" Content="{StaticResource ByValue}"/>
</TabControl>
</Grid>
</Page>

Categories