I have the following XAML and am trying to implement properties on two labels inside so I can set the captions on them when the controls are instantiated in the XAML. At a later stage this will be from code as well. Please can you tell me what am I missing about databinding, and what I should be doing? I am quite new to WPF.
<Window x:Class="WpfApplication1.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:local="clr-namespace:WpfApplication1"
xmlns:ctrl="clr-namespace:xCtrl"
Title="MainWindow" Width="525" ResizeMode="NoResize" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Window.Resources>
<p:Style TargetType="ctrl:BSHintButton" x:Key="BSStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="xgrid" Height="50" Width="500" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="430
"/>
</Grid.ColumnDefinitions>
<Label x:Name="xLabel" Content="{Binding Caption}"
Grid.Column="1" HorizontalAlignment="Left"
Margin="10,8,0,0" VerticalAlignment="Top"
Grid.RowSpan="2" Width="Auto"
FontFamily="Calibri" FontSize="14"
FontWeight="Bold" Height="Auto"/>
<Label x:Name="xHint" Content="{Binding HintText}"
Foreground="DarkCyan" Grid.Column="1"
HorizontalAlignment="Left" Height="Auto"
Margin="10,-2,0,0" Grid.Row="1"
VerticalAlignment="Top" Width="Auto"
FontFamily="Calibri" FontSize="14"/>
<Rectangle x:Name="xFocus"
Stroke="Orange"
StrokeThickness="1"
RadiusX = "4" RadiusY="4" Grid.RowSpan="2" Grid.ColumnSpan="2" IsEnabled="True" Visibility="Hidden"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="xgrid" Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.504,1.5" StartPoint="0.504,0.01">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Gold" Offset="0.1"/>
<GradientStop Color="White" Offset="0.8"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter TargetName="xFocus" Property="Visibility"
Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ctrl:OfficeBSHintButton" BasedOn="{StaticResource BSStyle}" />
</Window.Resources>
<StackPanel Orientation="Vertical" Margin="0,0,0,0.001">
<ctrl:BSHintButton x:Name="Button1" Tag ="2" Click="Button1_Click" Caption="caption1" HintText="Hint1">
</ctrl:BSHintButton>
<ctrl:BSHintButton x:Name="Button2" Tag ="1" Click="Button1_Click" Caption="caption1" HintText="Hint2">
</ctrl:BSHintButton>
</StackPanel>
</Window>
Code for the class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace xCtrl
{
public class BSHintButton : Button
{
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption",
typeof(string),
typeof(Label)
);
public static readonly DependencyProperty HintTextProperty =
DependencyProperty.Register("HintText",
typeof(string),
typeof(Label)
);
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
public string HintText
{
get { return (string)GetValue(HintTextProperty); }
set { SetValue(HintTextProperty, value); }
}
}
}
Without a good, minimal, complete code example, it can be difficult or impossible to know for sure what the problem is, never mind test a solution. But looking over your code, the most obvious thing I notice wrong with it is that you are declaring your dependency properties incorrectly.
The owner of the dependency property is the type in which the property will be declared, not the type which you expect to use as the target for a binding.
So your code should look like this instead:
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption",
typeof(string),
typeof(BSHintButton)
);
public static readonly DependencyProperty HintTextProperty =
DependencyProperty.Register("HintText",
typeof(string),
typeof(BSHintButton)
);
Having the wrong owner may prevent WPF from correctly handling the binding declared in the XAML. It's my hope and expectation that fixing your code as above will allow the binding to work.
If not though, please edit your question so that it includes a good code example. Without one, it's not possible to example your specific problem "in situ", to debug it properly.
Speaking of debugging, you should also get into the habit of checking the debug output for your program. When binding doesn't work, often WPF has emitted one or more error messages to the debug output. Some of the time, these messages are even helpful. :)
Related
Background
I'm creating a set of custom activities that perform simple actions that aren't found in UiPath's core activity set. I'm planning on extending these activities to include some of my own image processing and Machine Learning algorithms.
Currently I'm starting out small and have created a NativeActivity that strips a MailMessage of its attachments. The problem is that in UiPath it looks like this:
Problem
I want to circulate this activity, so I really need to have it look much better than this! I have tried looking at sites that do WorkFlow Foundation activity designs, but each time I successfully compile, package and install my project it looks the same as above. I would like my activity to reflect the xaml design below:
<sap:ActivityDesigner x:Class="LarcAI.MailActivityDesigner.MailActivity"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation"
xmlns:Model="clr-namespace:System.Activities.Presentation.Model;assembly=System.Activities.Presentation"
xmlns:MailActivityLibrary="clr-namespace:LarcAI.MailActivity;assembly=MailActivityLibrary">
<sap:ActivityDesigner.Resources>
<ResourceDictionary x:Uid="ResourceDictionary_1">
<sapc:ArgumentToExpressionConverter x:Key="ArgumentToExpressionConverter" />
<sapc:ModelToObjectValueConverter x:Key="ModelToObjectValueConverter" />
<DataTemplate x:Key="Collapsed">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="0" Grid.Column="0" Margin="5" Text="Mail" />
<sapv:ExpressionTextBox HintText="Enter a VB Expression" Expression="{Binding Path=ModelItem.Text, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In }" ExpressionType="s:String" Grid.Row="0" Grid.Column="1" OwnerActivity="{Binding Path=ModelItem}" Width="300" Margin="0,5" MaxLines="1" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Expanded">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="0" Grid.Column="0" Margin="5" Text="Mail" />
<sapv:ExpressionTextBox HintText="Enter a VB Expression" Expression="{Binding Path=ModelItem.Text, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In }" ExpressionType="s:String" Grid.Row="0" Grid.Column="1" OwnerActivity="{Binding Path=ModelItem}" Width="300" Margin="0,5" MaxLines="1" />
</Grid>
</DataTemplate>
<Style x:Key="ExpandOrCollapsedStyle" TargetType="{x:Type ContentPresenter}">
<Setter Property="ContentTemplate" Value="{DynamicResource Expanded}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShowExpanded}" Value="false">
<Setter Property="ContentTemplate" Value="{DynamicResource Collapsed}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</sap:ActivityDesigner.Resources>
<sap:ActivityDesigner.Icon>
<DrawingBrush>
<DrawingBrush.Drawing>
<ImageDrawing>
<ImageDrawing.Rect>
<Rect Location="0,0" Size="25,25" ></Rect>
</ImageDrawing.Rect>
<ImageDrawing.ImageSource>
<BitmapImage UriSource="Images/remove_attachment.png" />
</ImageDrawing.ImageSource>
</ImageDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</sap:ActivityDesigner.Icon>
<Grid>
<ContentPresenter Style="{DynamicResource ExpandOrCollapsedStyle}" Content="{Binding}" />
</Grid>
Help
If someone can please explain how to link a xaml file to my NativeActivities, preferably in a step-wise solution, that would be great!
I found out something that might be helpful to you.
I wanted to create a variation of the log activity. If I copy the properties from the UiPath "Log Message" activity, and then add their LogDesigner class as attribute to my class, then I get the same look and feel of my custom activity as UiPath has on their native version.
[Designer(typeof(UiPath.Core.Activities.Design.LogDesigner))]
public class LogMessage : CodeActivity, IRegisterMetadata
{
[Category("Input")]
public UiPath.Core.Activities.CurentLogLevel Level { get; set; }
[Category("Input")]
public InArgument<System.String> Message { get; set; }
[Category("Input")]
public InArgument<System.String> LogFilePath { get; set; }
So my activity now looks like this in UiPath Studio:
I highlighted a new property I added to the activity (to show it's not the original).
I know it does not answer your question on how to link custom xaml to your activity, but maybe this information could be helpful as to avoid the default look of custom activities.
So, I have created a UserControl which really is an "advanced button".
I have implemented Dependency Properties, one of which is ICommand, that is supposed to be bindable further when control is used in an actual Window.
However, for some reason the Command doesn't work.
When I tried an exact same approach on a regular button, everything worked fine (thus it's not the fault of my DelegateCommand implementation or my ViewModel).
I tried to followup on why the bound command doesn't fire, but I couldn't find a reliable reason for it not to.
Here is my UserControl XAML:
<Window x:Class="NoContact.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:NoContact"
mc:Ignorable="d"
xmlns:userControls="clr-namespace:NoContact.UserControls"
Title="MainWindow" Height="800" Width="960" Background="#22282a">
<Border BorderThickness="1" BorderBrush="#ffcd22" Margin="10,10,10,10">
<Grid>
<Button HorizontalContentAlignment="Stretch" Foreground="{Binding ElementName=ImageButtonUC, Path=Foreground}"
Background="{Binding ElementName=ImageButtonUC, Path=Background}">
<DockPanel Width="{Binding ElementName=ImageButtonUC, Path=ActualWidth}">
<Image Source="{Binding ElementName=ImageButtonUC, Path=Image}" DockPanel.Dock="Left"
Height="{Binding ElementName=ImageButtonUC, Path=ActualHeight, Converter={StaticResource HeightConverter}}"
Width="{Binding ElementName=ImageButtonUC, Path=ActualWidth, Converter={StaticResource HeightConverter}}" VerticalAlignment="Center"/>
<TextBlock Text="{Binding ElementName=ImageButtonUC, Path=Text}" FontSize="17" VerticalAlignment="Center" />
</DockPanel>
</Button>
</Grid>
<UserControl.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And here is my UserControl code-behind:
public partial class ImageButton : UserControl
{
// OTHER IRRELEVANT CLASS PARAMETERS ARE HERE
public ICommand ClickCommand
{
get { return (ICommand)GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
// OTHER IRRELEVANT DEPENDENCY PROPERTIES ARE HERE
public static DependencyProperty ClickCommandProperty =
DependencyProperty.Register("ClickCommand", typeof(ICommand), typeof(ImageButton));
public ImageButton()
{
InitializeComponent();
}
}
Finally, this is how I use my control:
<userControls:ImageButton x:Name="phoneButton" ClickCommand="{Binding Path=MyButtonClickCommand}" Style="{StaticResource phoneImageButtonUCStyle}" Text="Telefon" Width="200" Height="100" VerticalAlignment="Top" />
The DataContext of the control is set on a stackpanel, that is wrapped around my control. I have also tried setting it directly on the control, no effect.
Again, doing the same on a regular button works just fine.
I have finally solved the problem - and it was pretty trivial.
I had ommited the most important binding - UserControl's button Command to UserControl's Dependency property.
Changing it like this made it work:
<Button HorizontalContentAlignment="Stretch" Foreground="{Binding ElementName=ImageButtonUC, Path=Foreground}"
Background="{Binding ElementName=ImageButtonUC, Path=Background}"
Command="{Binding ElementName=ImageButtonUC, Path=ClickCommand}">
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>
I have an issue with ListBoxItems. I am trying to make all controls in the ListBoxItem select it as well, so clicking on a TextBox, Label, etc will select the ListBoxItem. Pretty simple so far.
I am also changing the ListBoxItem Template to change the selection visualization from highlighting the background to just drawing a border. Also pretty simple.
The combination of these two, however, seems to cause some really irritating issues with MouseDown and PreviewMouseDown, specifically in my case regarding Labels in a Grid, where one creates a "void" occupied by Grid space.
Using snoop, I can see the PreviewMouseDown event stopping at the ScrollViewer inside the ListBox, and not going all the way to the ListBoxItem.
XAML:
<Window x:Class="ListBoxClickThroughTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350">
<Grid>
<ListBox ItemsSource="{Binding Items}"
SelectionMode="Single">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Name="VerySuperLongLabel"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
Content="VerySuperLongLabel"
Padding="0" />
<TextBox Name="Textbox1"
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Right"
Text="Textbox1 Text" />
<Label Name="ShortLabel"
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Left"
Content="ShortLabel"
Padding="0" />
<TextBox Name="Textbox2"
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Right"
Text="Textbox2 Text" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="PreviewMouseDown"
Handler="ListBoxItem_PreviewMouseDown" />
<EventSetter Event="MouseDown"
Handler="ListBoxItem_PreviewMouseDown" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd"
BorderThickness="1">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Bd" Property="BorderBrush" Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</Window>
Code-behind:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace ListBoxClickThroughTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
Items = new List<string>() { "1", "2" };
InitializeComponent();
DataContext = this;
}
public List<string> Items { get; set; }
private void ListBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var listBoxItem = (ListBoxItem)sender;
listBoxItem.IsSelected = true;
}
}
}
However, if I remove the Template setter, all is well. Is there some magic in the template I'm missing? I tried renaming the border to "Bd" as that was what the default template border was named, but no luck. Any ideas?
If you change the horizontal alignment of the labels from "Left" to "Stretch" this will fix the problem and keep the visual formatting the same.
Mousedown events only work in areas where elements exist. By having the labels at a "left" horizontal alignment, you are creating the "void" you mentioned, where no element exists at that level that can be clicked. To visually see the difference, try temporarily setting the background property of the label elements that are giving you problems, and you'll see the element doesn't extend all the way to the textbox.
Is there something standard and similar to the Windows Forms SplitContainer in WPF?
I'm a bit lost with Grids because controls seem not to be inside the cells but over them :s.
I did some googling but I don't know exactly what to write in the search field...
Sample of how to make an Horizontal SplitContainer using a Grid and a GridSplitter like SnowBear said:
<Window x:Class="JobFinder1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="JobFinder1" Height="350" Width="525" AllowsTransparency="False">
<Grid Name="gridMain">
<Grid.RowDefinitions>
<RowDefinition Height="26" />
<RowDefinition />
<RowDefinition Height="1"/>
<RowDefinition />
</Grid.RowDefinitions>
<ToolBar Height="26" VerticalAlignment="Stretch" HorizontalAlignment="Left" />
<ListView Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<GridSplitter Background="DarkGray" ResizeDirection="Rows" Grid.Row="2"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ResizeBehavior="PreviousAndNext" />
<ListView Grid.Row="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</Window>
Grid + GridSplitter at your service
Grid + GridSplitter are a pain. Well they're easy but kinda make for ugly Xaml and sometimes hard to maintain if you're in the design phase and rearranging things a lot.
You may be looking for something like this
http://wpfsplitcontainer.codeplex.com/
Note: check the license before using
Here's a simple control, needs a bit more love, but hey, does it's
job ...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Markup;
namespace Infinity.Shell.Controls.Docking
{
/// <summary>
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
///
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:Infinity.Shell.Controls.Docking"
///
///
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:MyNamespace="clr-namespace:Infinity.Shell.Controls.Docking;assembly=Infinity.Shell.Controls.Docking"
///
/// You will also need to add a project reference from the project where the XAML file lives
/// to this project and Rebuild to avoid compilation errors:
///
/// Right click on the target project in the Solution Explorer and
/// "Add Reference"->"Projects"->[Browse to and select this project]
///
///
/// Step 2)
/// Go ahead and use your control in the XAML file.
///
/// <MyNamespace:SplitContainer/>
///
/// </summary>
public class SplitContainer : Control
{
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(SplitContainer), new PropertyMetadata(Orientation.Horizontal));
public UIElement FirstChild
{
get { return (UIElement)GetValue(FirstChildProperty); }
set { SetValue(FirstChildProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FirstChildProperty =
DependencyProperty.Register("FirstChild", typeof(UIElement), typeof(SplitContainer), new PropertyMetadata(null));
public UIElement SecondChild
{
get { return (UIElement)GetValue(SecondChildProperty); }
set { SetValue(SecondChildProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SecondChildProperty =
DependencyProperty.Register("SecondChild", typeof(UIElement), typeof(SplitContainer), new PropertyMetadata(null));
static SplitContainer()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitContainer), new FrameworkPropertyMetadata(typeof(SplitContainer)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
}
}
<Style TargetType="{x:Type dock:SplitContainer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dock:SplitContainer}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions> <!--Horizontal -->
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid x:Name="PART_FirstChildGrid"
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="3">
<ContentPresenter Content="{TemplateBinding FirstChild}"/>
</Grid>
<GridSplitter x:Name="PART_Splitter"
Grid.Column="1"
Grid.Row="0"
Grid.RowSpan="3"
Grid.ColumnSpan="1"
Width="5"
VerticalAlignment="Stretch"
HorizontalAlignment="Center"
ShowsPreview="True"/>
<Grid x:Name="PART_SecondChildGrid"
Grid.Column="2"
Grid.Row="0"
Grid.RowSpan="3">
<ContentPresenter Content="{TemplateBinding SecondChild}"/>
</Grid>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter TargetName="PART_FirstChildGrid" Property="Grid.Column" Value="0"/>
<Setter TargetName="PART_FirstChildGrid" Property="Grid.Row" Value="0"/>
<Setter TargetName="PART_FirstChildGrid" Property="Grid.ColumnSpan" Value="3"/>
<Setter TargetName="PART_FirstChildGrid" Property="Grid.RowSpan" Value="1"/>
<Setter TargetName="PART_Splitter" Property="Grid.Column" Value="0"/>
<Setter TargetName="PART_Splitter" Property="Grid.Row" Value="1"/>
<Setter TargetName="PART_Splitter" Property="Grid.ColumnSpan" Value="3"/>
<Setter TargetName="PART_Splitter" Property="Grid.RowSpan" Value="1"/>
<Setter TargetName="PART_Splitter" Property="VerticalAlignment" Value="Center"/>
<Setter TargetName="PART_Splitter" Property="HorizontalAlignment" Value="Stretch"/>
<Setter TargetName="PART_Splitter" Property="Width" Value="Auto"/>
<Setter TargetName="PART_Splitter" Property="Height" Value="5"/>
<Setter TargetName="PART_SecondChildGrid" Property="Grid.Column" Value="0"/>
<Setter TargetName="PART_SecondChildGrid" Property="Grid.Row" Value="2"/>
<Setter TargetName="PART_SecondChildGrid" Property="Grid.ColumnSpan" Value="3"/>
<Setter TargetName="PART_SecondChildGrid" Property="Grid.RowSpan" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<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:Infinity.Shell.Controls.Text;assembly=Infinity.Shell"
xmlns:dock="clr-namespace:Infinity.Shell.Controls.Docking;assembly=Infinity.Shell"
x:Class="Infinity.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="499.573" Background="#FF1E1E1E">
<Grid>
<dock:SplitContainer Orientation="Vertical">
<dock:SplitContainer.FirstChild>
<dock:SplitContainer>
<dock:SplitContainer.FirstChild>
<Button Content="First Child Button"/>
</dock:SplitContainer.FirstChild>
<dock:SplitContainer.SecondChild>
<Button Content="Second Child Button"/>
</dock:SplitContainer.SecondChild>
</dock:SplitContainer>
</dock:SplitContainer.FirstChild>
<dock:SplitContainer.SecondChild>
<Button Content="First Splitter Second Child"/>
</dock:SplitContainer.SecondChild>
</dock:SplitContainer>
</Grid>
</Window>
Another one, without GPL license
http://www.codeproject.com/Articles/293396/Teh-Simple-but-Useful-WPF-Container
This GridSplitter tutorial from Microsoft is also very handy. Here is an expanded example, with both horizontal and vertical resizing. (Pay attention to GridSplitter's HorizontalAligment and ResizeDirection).
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition MinHeight="70" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition MinWidth="70" />
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0" Background="LightBlue" ></DockPanel>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" ResizeDirection="Columns" Height="Auto" Width="3" />
<Grid Grid.Column="2" Background="PaleGoldenrod" ></Grid>
</Grid>
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" ResizeDirection="Rows" Height="3" Width="Auto" />
<DockPanel Grid.Row="2" Background="Green" ></DockPanel>
</Grid>