WPF: Something standard and similar to the `SplitContainer`? - c#

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>

Related

Binding misunderstanding not getting properties set

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. :)

Inheritance On default style from Textbox not working and Share Dictionaries

(For the back story about this question please see Question Here)
To continue learning about WPF I am trying to Extend an TextBox Control and add a Dependency Property which can be used in a Style to put a border around my extended TextBox Control. To help me achieve this I am using this article basics-of-extending-a-wpf-control.
I have encountered two problems:
1. When I put the style into a Resource Dictionary and try to merge the Resource Dictionary with a file called "generic.xaml" (also tried "Generic.xaml") the style does not take, upon reading this question here trouble-referencing-a-resource-dictionary-that-contains-a-merged-dictionary I tried the solution of adding a Style referencing any control but it did not work (all code at the bottom of the question}. Can anyone explain why please?
2. To overcome the problem above I moved my Style into a Window Resource, but I encounted a different problem. I purposely left out the following code from the worked example in my Constructor of my Extended Control
static ExTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExTextBox),
new FrameworkPropertyMetadata(typeof(ExTextBox)));
}
as I was expecting my control to inherit the default style of the Standard TextBox, which includes the Properties BorderThickness and BorderBrush. However, when I try and Set these Properties with the Setter statement vs2010 says it can not find the Properties. Can someone explain why and if these Properties are not inherited do I have to use a Control Template in my Style to set up a Border around my Control?
My code from problem 1
MainWindow.xaml
<Window x:Class="Controls.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="StyleTriggersSample" Height="100" Width="300" xmlns:my="clr-namespace:Controls">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="138*" />
<ColumnDefinition Width="140*" />
</Grid.ColumnDefinitions>
<my:ExTextBox Height="23" HorizontalAlignment="Left" Margin="12,28,0,0" x:Name="exTextBox1" Text="ExTextBlock" VerticalAlignment="Top" Width="116" />
<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Left" Margin="14,30,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="textBox1" />
<Button Content="Test" Grid.Column="1" Height="23" HorizontalAlignment="Left" Margin="14,0,0,0" Name="button1" VerticalAlignment="Top" Width="114" Click="button1_Click" />
</Grid>
My Extended Control
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
namespace Controls
{
public class ExTextBox : TextBlock
{
public static DependencyProperty ErrorFlagProperty =
DependencyProperty.Register("ErrorFlag", typeof(bool),
typeof(ExTextBox),
new FrameworkPropertyMetadata((bool)false) );
public bool ErrorFlag
{
get { return (bool)GetValue(ErrorFlagProperty); }
set { SetValue(ErrorFlagProperty, value); }
}
static ExTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExTextBox),
new FrameworkPropertyMetadata(typeof(ExTextBox)));
}
}
}
Controls.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Controls:ExTextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="FontSize" Value="28" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="Silver" />
<Setter Property="Height" Value="50" />
<Setter Property="Width" Value="120" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="BorderThickness"
Value="5" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/TestContentStyle;component/Controls.xaml" />
</ResourceDictionary.MergedDictionaries>-->
<!-- Dummy Style, anything you won't use goes -->
<Style TargetType="{x:Type Rectangle}" />

WPF ListBoxItem ControlTemplate breaks some MouseDown/Selection

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.

Actually using the Tile Control in MahApps Metro?

I have been working with MahApps Metro UI for couple days now and i have realy enjoyed it. WHen looking through their documentation, i wanted to use the tile control and make something along the lines of this:
Their documentation, located on this page: http://mahapps.com/controls/tile.html , only tells me this:
The following XAML will initialize a Tile control with its Title set to "Hello!" and its Count set to 1.
<controls:Tile Title="Hello!"
TiltFactor="2"
Width="100" Height="100"
Count="1">
</controls:Tile>
When i entered this into my simple application, i get one small rectangle. How am i actually supposed to use the control to mimic the Windows 8 start screen with tiles?
I'm currently building a large application using the MahApps Metro library and it's amazing! In terms of getting an app to look like the Windows 8 start screen, heres quick example I whipped up.
XAML
<Window x:Class="Win8StartScreen.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="MainWindow" Height="513" Width="1138" WindowState="Maximized" WindowStyle="None" Background="#191970">
<Window.Resources>
<Style x:Key="LargeTileStyle" TargetType="mah:Tile">
<Setter Property="Width" Value="300" />
<Setter Property="Height" Value="125" />
<Setter Property="TitleFontSize" Value="10" />
</Style>
<Style x:Key="SmallTileStyle" TargetType="mah:Tile">
<Setter Property="Width" Value="147" />
<Setter Property="Height" Value="125" />
<Setter Property="TitleFontSize" Value="10" />
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="87*"/>
<ColumnDefinition Width="430*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="83*"/>
<RowDefinition Height="259*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Text="Start"
FontWeight="Light"
Foreground="White"
FontSize="30"
FontFamily="Segoe UI" />
<WrapPanel Grid.Row="1" Grid.Column="1" Width="940" Height="382" HorizontalAlignment="Left" VerticalAlignment="Top">
<mah:Tile Title="Mail" Style="{StaticResource LargeTileStyle}" Content="ImageHere" Background="Teal" Margin="3"/>
<mah:Tile Title="Desktop" Style="{StaticResource LargeTileStyle}" Margin="3">
<mah:Tile.Background>
<ImageBrush ImageSource="Images/windesktop.jpg" />
</mah:Tile.Background>
</mah:Tile>
<mah:Tile Title="Finance" Style="{StaticResource LargeTileStyle}" Background="Green" />
<mah:Tile Title="People" Style="{StaticResource LargeTileStyle}" Background="#D2691E" />
<mah:Tile Title="Weather" Style="{StaticResource LargeTileStyle}" Background="#1E90FF" />
<mah:Tile Title="Weather" Style="{StaticResource SmallTileStyle}" Background="#1E90FF" />
<mah:Tile Title="Store" Style="{StaticResource SmallTileStyle}" Background="Green" />
</WrapPanel>
</Grid>
</Window>
There are lots of ways to do this to make it cleaner and more reusable using styles and templates, but this was just a quick way to show the use of the Tiles control.

Getting ListBoxItem from item template control event

In the WPF XAML code below, if I am in the SelectTaskItemClick event for the templated Button, how do I get the ListBoxItem ItemSource object that is currently selected?
<!-- ListBox ITEMS -->
<TaskDash:ListBoxWithAddRemove x:Name="listBoxItems" Grid.Row="1" Grid.Column="3" Grid.RowSpan="3"
ItemsSource="{Binding}">
<!--ItemsSource="{Binding}" DisplayMemberPath="Description">-->
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=Selected}"/>
</Style>
<TaskDash:ListBoxWithAddRemove.ItemTemplate>
<DataTemplate>
<DockPanel>
<Button DockPanel.Dock="Left" Click="SelectTaskItemClick">SELECT</Button>
<TextBox DockPanel.Dock="Left" Name="EditableDescription" Text="{Binding Description}" Height="25" Width="100" />
<Button DockPanel.Dock="Left" Click="EditTaskItemClick">EDIT</Button>
</DockPanel>
</DataTemplate>
</TaskDash:ListBoxWithAddRemove.ItemTemplate>
</TaskDash:ListBoxWithAddRemove>
If I try to get the Parent or TemplateParent, it gives me the ContentPresenter or Style or something similar.
private void SelectTaskItemClick(object sender, RoutedEventArgs e)
{
Button taskItemButton = (Button) e.OriginalSource;
ContentPresenter taskItem = (ContentPresenter) taskItemButton.TemplatedParent;
taskItem = (ContentPresenter)taskItemButton.TemplatedParent;
Style taskItem2 = taskItem.TemplatedParent;
taskItem2 = taskItem.TemplatedParent;
DependencyObject taskItem3 = taskItem2.Parent;
//DependencyObject taskItem3 = taskItem2.TemplatedParent;
//TaskItem taskItemObj = taskItem2;
}
In the code above, I'm guessing it is grabbing that from App.XAML where that custom ListBoxWithAddRemove control is defined. How do I traverse the actual form's XAML instead [the first code shown above]?
<Style x:Key="{x:Type TaskDash:ListBoxWithAddRemove}" TargetType="{x:Type TaskDash:ListBoxWithAddRemove}">
<Setter Property="Margin" Value="3" />
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TaskDash:ListBoxWithAddRemove}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Grid.Row="0"
Click="DeleteControlClick">Delete</Button>
<Button Grid.Column="1" Grid.Row="0"
Click="AddControlClick">Add</Button>
<Border
Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"
Name="Border"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1"
CornerRadius="2">
<ScrollViewer
Margin="0"
Focusable="false">
<StackPanel Margin="0" IsItemsHost="True" />
</ScrollViewer>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You can use the VisualTreeHelper to walk up the tree and stop if you have an object of the right type, e.g.
private void SelectTaskItemClick(object sender, RoutedEventArgs e)
{
var b = sender as Button;
DependencyObject item = b;
while (item is ListBoxItem == false)
{
item = VisualTreeHelper.GetParent(item);
}
var lbi = (ListBoxItem)item;
//...
}
(If you just want to select the item that can (and should) just be done via the established binding, e.g.)
private void SelectTaskItemClick(object sender, RoutedEventArgs e)
{
// The DataContext should be an item of your class that should
// have a Selected property as you bind to it in a style.
var data = (sender as FrameworkElement).DataContext as MyClass;
data.Selected = true;
}
Assuming that
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=Selected}"/>
</Style>
works the way that it appears you intended, you should be able to loop through the items in your DataContext used as the ItemsSource for your listbox and check the Selected property of each to find the one currently selected. The more typical way of determining the selected item from a ListBox is by using listBox.SelectedItem where listBox is a variable that refers to the ListBox in question. Alternatively you might be able to access it off the sender parameter to the SelectTaskItemClick method. Another method you might try is helper methods to traverse the visual tree such as described at The Coding Bloke, and The Code Project - LINQ to Visual Tree.

Categories