Binding a command doesn`t work - wpf

I'm beginner in WPF development.
I create some window with frame inside.
I wrote a template for the frame and i`m trying to bind to the buttons inside the freame template commands without success.
see the next code:
<Grid.Resources>
<ControlTemplate TargetType="Frame" x:Key="NavigationButtonsTemplates">
<DockPanel>
<StackPanel
Margin="7"
Orientation="Horizontal"
DockPanel.Dock="Bottom"
HorizontalAlignment="Right"
>
<Button
Content="Back"
Command="{x:Static NavigationCommands.BrowseBack}"
Margin="5,0,5,0" Width="80" />
<Button
Content="Next"
Command="{Binding Path=NavigateToPersonalDataCommand}"
Margin="5,0,5,0" Width="80"></Button>
</StackPanel>
<Border
BorderBrush="LightBlue"
Margin="7,8,9,0"
BorderThickness="7"
Padding="5"
CornerRadius="7"
Background="White"
>
<ContentPresenter />
</Border>
</DockPanel>
</ControlTemplate>
</Grid.Resources>
It seems that the binding of the buttons is not working.
When i put the buttons outside the tag it`s work perfectly.
How can I bind a command to a button which located in ?
Thanks

Your ControlTemplate needs to have a DataContext. Try changing the DockPanel element to bind to the DataContext property on the templated frame. This assumes that the Frame that is being templated has a valid DataContext.
<DockPanel DataContext="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}">
or
<DockPanel DataContext="{TemplateBinding DataContext}">
Edit:
After trying to run your code I found the problem. The properties you are binding to in XAML do not match the properties on your view model.
Changing your XAML to bind to the property names on your view model fixes the problem.
From this:
Command="{Binding Path=NavigateNext}"
Command="{Binding Path=NavigateToPersonalData}"
To this:
Command="{Binding Path=BrowseNext}"
Command="{Binding Path=NavigateToPersonalDataCommand}"
to match your view model properties:
public ICommand BrowseNext
{
get
{
return m_BrowseNext;
}
set
{
m_BrowseNext = value;
}
}
public ICommand NavigateToPersonalDataCommand
{
get
{
return m_PersonalDataCommand;
}
set
{
m_PersonalDataCommand = value;
}
}

This is the Xaml code:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestProj"
x:Class="TestProj.MainView"
x:Name="Window"
Title="MainView"
Width="640" Height="480">
<Window.DataContext>
<local:NavigationViewModel/>
</Window.DataContext>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="0.25*"/>
<RowDefinition Height="0.25*"/>
<RowDefinition Height="0.25*"/>
<RowDefinition Height="0.25*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.20*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.Resources>
<ControlTemplate TargetType="Frame" x:Key="NavigationButtonsTemplates">
<DockPanel DataContext="{TemplateBinding DataContext}">
<StackPanel
Margin="7"
Orientation="Horizontal"
DockPanel.Dock="Bottom"
HorizontalAlignment="Right"
>
<Button
Content="Back"
Command="{Binding Path=NavigateNext}"
Margin="5,0,5,0" Width="80" />
<Button
Content="Next"
Command="{Binding Path=NavigateToPersonalData}"
Margin="5,0,5,0" Width="80"></Button>
</StackPanel>
<Border
BorderBrush="LightBlue"
Margin="7,8,9,0"
BorderThickness="7"
Padding="5"
CornerRadius="7"
Background="White"
>
<ContentPresenter />
</Border>
</DockPanel>
</ControlTemplate>
</Grid.Resources>
<Button Content="Opening" Grid.Row="0"></Button>
<Button Content="Personal Data" Grid.Row="1"></Button>
<Button Content="Business Data" Grid.Row="2"> </Button>
<Button Content="Summery Report" Grid.Row="3"></Button>
<DockPanel Grid.Column="2" Grid.RowSpan="4">
<Frame x:Name="mainFrame" Template="{StaticResource NavigationButtonsTemplates}"/>
</DockPanel>
</Grid>
</Window>
Here how i wrote the commands:
class NavigationViewModel
{
private ICommand m_BrowseNext;
public ICommand BrowseNext
{
get
{
return m_BrowseNext;
}
set
{
m_BrowseNext = value;
}
}
private ICommand m_PersonalDataCommand;
public ICommand NavigateToPersonalDataCommand
{
get
{
return m_PersonalDataCommand;
}
set
{
m_PersonalDataCommand = value;
}
}
public NavigationViewModel()
{
BrowseNext = new RelayCommand(new Action<object>(NavigateNext));
NavigateToPersonalDataCommand = new RelayCommand(new Action<object>(NavigateToPersonalData));
}
public void NavigateToPersonalData(object obj)
{
MainView.Instance.GetMainFrame.Navigate(Pages.Opening.Instance);
}
public void NavigateNext(object obj)
{
MainView.Instance.GetMainFrame.Navigate(Pages.PersonalData.Instance);
}
}
When i move the buttons outside the the commands works great so i think the problem is in the XAML.
Thanks!!!

Related

UserControl child fill Window

I have a UserControl that is a portion of a wpf window.
<Window>
<Grid>
<!--some other display elements would be here-->
<local:MyUserControl x:Name="Foo" Padding="0,42,0,50"/>
</Grid>
</Window>
Inside MyUserControl I have an element that is a gallery that is normally hidden, but when visible, it should fill the entire screen.
<UserControl>
<Grid>
<!--main display elements would be here-->
<Grid Name="Gallery" Visibility="Hidden">
<Rectangle Fill="Black" Opacity="0.75"/>
<TextBlock Name="GalleryLabel" Foreground="White" TextAlignment="Center">Current Image Title</TextBlock>
<Button Name="CloseGallery" Style="{DynamicResource WhiteTextButton}" Margin="0,0,0,0" Height="25" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Click="GalleryClose_OnClick">X</Button>
<Image Name="GalleryImage" Margin="25"/>
</Grid>
</Grid>
</UserControl>
How can I set the Gallery to fill the entire Window rather than just the UserControl?
I was able to get it to work by adding Margin="0,-42,0,-50" to Gallery, but I don't like that solution. I would rather do something that doesn't involve hard-coding values in the UserControl so that I would be able to have more flexiblility in using it.
Normally it looks like:
where the green Foo area is MyUserControl, and the rest of the things in the window are other elements.
At certain points, I have a gallery display an image, which should fill the entire screen like:
which should fill the entire screen and have a black opaque overlay, along with an image displayed on top of the overlay.
Remove the Padding...
You can use:
<local:MyUserControl x:Name="Foo" VerticalAlignment="Stretch" HorizontalAligment="Stretch"/>
EDIT
I may admit that I still am not sure what you want. But I made something which does the same as you showed in the pictures. But be aware that this as an opinion based question, and the answer is my opinion, there are a lot of ways to achieve this. And this is only one of them:
MainWindow
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myProject="clr-namespace:MyProject"
Title="MainWindow" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Background="Gainsboro" Grid.Row="0">
<StackPanel Orientation="Horizontal">
<Button Content="Show gallery" Width="100" Margin="10"/>
<Button Content="Do something else" Width="150" Margin="10"/>
</StackPanel>
</Border>
<myProject:SomeOtherStuff Grid.Row="1" />
<Border Grid.Row="2" Background="Gainsboro">
<StackPanel Orientation="Horizontal">
<Label Content="Buttom area, can be used for something else"/>
</StackPanel>
</Border>
<myProject:GalleryUserControl x:Name="GalleryUserControl" Grid.Row="0" Grid.RowSpan="3" Visibility="Hidden"/>
</Grid>
SomeOtherStuff UserControl
<UserControl x:Class="MyProject.SomeOtherStuff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="Green">
<Label VerticalAlignment="Center" HorizontalAlignment="Center" Content="Foo" FontSize="30" FontFamily="Verdana"/>
</Grid>
Gallery UserControl
<UserControl x:Class="XamDataGrid.GalleryUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="500" Width="800">
<Grid Background="#99000000">
<TextBlock Text="Currect image title" Foreground="White" TextAlignment="Center" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<Button Name="CloseGallery" Margin="0,0,0,0" Content="X" Height="25" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Click="GalleryClose_OnClick"/>
<Image Margin="30"/>
</Grid>
An Adorner will be best for you. As it floats above everything else and remains outside the VisualTree too.
UserControl2
<UserControl2 ...>
<Grid>
<Grid Background="#FF709FA6" Opacity="0.3" Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
</Grid>
<Grid Width="600" Height="700">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF37DAEA" Offset="0"/>
<GradientStop Color="#FFE84242" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<!-- Gallery controls -->
<Button Content="Welcome to Gallery" HorizontalAlignment="Left" IsHitTestVisible="True" Margin="112,126,0,0" VerticalAlignment="Top" Height="68" FontSize="48" Click="Button_Click_1"/>
<TextBlock HorizontalAlignment="Left" Margin="83,42,0,0" TextWrapping="Wrap" Text="UserControl2" VerticalAlignment="Top" Foreground="#FF1B0D0D"/>
</Grid>
</Grid>
</UserControl2>
UserControl1
Code :
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// get root Window
DependencyObject current = LogicalTreeHelper.GetParent(this);
while (!(current is Window))
current = LogicalTreeHelper.GetParent(current);
Window root = current as Window;
IEnumerable children = LogicalTreeHelper.GetChildren(root);
Panel p = null;
foreach (var child in children)
{
p = child as Panel; // get first container panel in Window
break;
}
// Create UserControl2 to add to the adorner
UserControl2 ctrlGallery = new UserControl2();
AdornerLayer layer = AdornerLayer.GetAdornerLayer(p);
GalleryAdorner adorner = new GalleryAdorner(p, ctrlGallery);
layer.Add(adorner);
}
}
public class GalleryAdorner : Adorner
{
private Control _child;
VisualCollection collection;
public GalleryAdorner(UIElement elem, Control child)
: base(elem)
{
collection = new VisualCollection(this);
_child = child;
collection.Add(_child);
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
protected override Visual GetVisualChild(int index)
{
if (index != 0) throw new ArgumentOutOfRangeException();
return collection[0];
}
protected override Size MeasureOverride(Size constraint)
{
_child.Measure(constraint);
return _child.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
_child.Arrange(new Rect(new Point(0, 0), finalSize));
return new Size(_child.ActualWidth, _child.ActualHeight);
}
}

Images aren't displayed in WPF ListBox

I'm trying to display images in a ListView. The images are being loaded through Entity Framework from a SQL Server database. Somehow the images don't get displayed in the ListView and I can't figure out why.
I've created a test Visual Studio project.
The images are stored in an ObservableCollection<ImageViewModel>:
private ObservableCollection<ImageViewModel> images;
public ObservableCollection<ImageViewModel> Images
{
get { return images; }
set { images = value; }
}
Please let me know if you need any further details. I'm grateful for any hints that I could try out.
I'll add a screenshot as soon as I have 10 reputation.
The ImageViewModel class looks like this:
using Common;
using TestImage.Model;
namespace TestImage.ViewModel
{
class ImageViewModel : ObservableObject
{
public ImageViewModel()
{
image = new ImageTable() { Name = "unknown"};
}
#region Properties
private ImageTable image;
public ImageTable Image
{
get { return image; }
set { image = value; }
}
public string Name
{
get { return image.Name; }
set
{
if (image.Name != value)
{
image.Name = value;
RaisePropertyChanged("Name");
}
}
}
public byte[] ImageBinary
{
get { return image.ImageBinary; }
set
{
if (image.ImageBinary != value)
{
image.ImageBinary = value;
RaisePropertyChanged("ImageBinary");
}
}
}
#endregion
}
}
The MainWindow.xaml looks like this:
<Window x:Class="TestImage.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestImage.ViewModel"
Title="MainWindow" Height="375.6" Width="525">
<Window.DataContext>
<local:ImageLibrary/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="4" CornerRadius="5" Margin="6">
<Image Source="{Binding Images}" Stretch="Fill" Width="100" Height="120" />
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
</Style>
</Window.Resources>
<Grid Margin="0,0,0.4,1.4">
<ListBox Name="ListBoxImages" ItemsSource="{Binding Images}" Margin="413,40,20,31.6"/>
<Image Name="ImageSource" Source="{Binding SelectedImage}" HorizontalAlignment="Left" Height="148" Margin="14,90,0,0" VerticalAlignment="Top" Width="174"/>
<Button Name="ButtonPick" Content="Pick" HorizontalAlignment="Left" Margin="14,266,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.563,-0.731" Command="{Binding CommandPickImage}"/>
<Button Name="ButtonSave" Content="Save" HorizontalAlignment="Left" Margin="113,266,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.563,-0.731" Command="{Binding CommandSaveImage}" />
<Label Name="LabelFileName" Content="FileName" HorizontalAlignment="Left" Margin="14,40,0,0" VerticalAlignment="Top" Width="60"/>
<TextBox Name="TextBoxFileName" HorizontalAlignment="Left" Height="26" TextWrapping="Wrap" VerticalAlignment="Top" Width="314" Margin="79,40,0,0" Text="{Binding SelectedFileName}"/>
<Button Name="ButtonLoad" Content="Load" HorizontalAlignment="Left" Margin="113,293,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.563,-0.731" Command="{Binding CommandLoadImage}" />
<Image Source="{Binding SelectedImageDatabase}" HorizontalAlignment="Left" Height="148" Margin="193,90,0,0" VerticalAlignment="Top" Width="100"/>
</Grid>
</Window>
The binding in the ListBox ItemTemplate should be to the ImageViewModel's ImageBinary property, instead of Images:
<DataTemplate>
<Border ...>
<Image Source="{Binding ImageBinary}" .../>
</Border>
</DataTemplate>

Caliburn.Micro example with AvalonDock not working

This is a newbie question about using Avalon Dock and Caliburn.Micro together. First I got a simple example of Caliburn.Micro working, taken from the Mindscape blog's excellent tutorial on Caliburn Micro. This example consists of a main window called MainShellView:
<UserControl x:Class="TestApp.MainShellView"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Width="300" Height="300" Background="LightBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentControl Name="ColorModel" Margin="10"/>
<Rectangle Width="100" Height="100" Fill="{Binding Color}" Grid.Column="1"/>
</Grid>
</UserControl>
where ColorView is made of three radio buttons:
<UserControl x:Class="TestApp.ColorView">
<Grid>
<RadioButton Name="Red" Content="Red" Foreground="White"
VerticalAlignment="Center" HorizontalAlignment="Center" />
<RadioButton Name="Green" Content="Green" Foreground="White"
VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="1" />
<RadioButton Name="Blue" Content="Blue" Foreground="White"
VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="2" />
</Grid>
</UserControl>
The class behind MainShellView has a Color property:
public class MainShellViewModel : PropertyChangedBase, IHandle<ColorEvent>
{
public SolidColorBrush Color
{
get { ... }
set { ... }
}
}
and Caliburn.Micro sets the color of the rectangle on the right in MainShellView to the color selected by the radio buttons:
[Export(typeof(ColorViewModel))]
public class ColorViewModel
{
public void Red()
{
_events.PublishOnUIThread(new ColorEvent(new SolidColorBrush(Colors.Red)));
}
public void Green() { /* something similar */ }
public void Blue() { /* something similar */ }
}
I then tried replacing the Grid in the MainShellView by an AvalonDock layout, with the ColorView in one document and the colored rectangle in another document:
<UserControl x:Class="TestApp.MainShellView"
xmlns:local="clr-namespace:TestApp"
d:DesignHeight="300" d:DesignWidth="300">
<avalon:DockingManager>
<avalon:LayoutRoot>
<avalon:LayoutPanel>
<avalon:LayoutDocumentPane>
<avalon:LayoutDocument Title="Document 1">
<ContentControl Name="ColorModel"/>
</avalon:LayoutDocument>
</avalon:LayoutDocumentPane>
<avalon:LayoutDocumentPane>
<avalon:LayoutDocument Title="Document 2">
<Rectangle Width="100" Height="100" Fill="{Binding Color}" Grid.Column="1"/>
</avalon:LayoutDocument>
</avalon:LayoutDocumentPane>
</avalon:LayoutPanel>
</avalon:LayoutRoot>
</avalon:DockingManager>
</UserControl>
However the ColorView doesn't appear on the left hand side. The Caliburn binding also fails - stepping through the Caliburn source code, this is because the VisualTreeHelper can't see the ColorView.
I've abbreviated the source code above, and put the full source in https://github.com/BobMortimer/SO_Question1 .
I'm certainly doing something stupid. So why is this not working, and how can I fix it?
I think you are missing 2 overrides in bootstrapper for
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return container.GetExportedValues<object>(
AttributedModelServices.GetContractName(serviceType));
}
and
protected override void BuildUp(object instance)
{
container.SatisfyImportsOnce(instance);
}
surprisingly enough it ran but didn't do what it was suppose to based on the source you provided in github. As you said on the debugger it dies, was it IoC not Initialized?

listBox DataTemplate not picking up values

I am learning to use listBox in WPF with dataTemplate using the examples from MSDN, I can render a listBox bound to an ObservableCollection as a source and by overriding the ToString method.
However, I need to render an image and some texblocks for every item. Here's my XAML:
<Grid x:Class="MyAddin.WPFControls"
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:c="clr-namespace:MyAddin"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Background="Transparent"
HorizontalAlignment="Stretch" Width="auto"
Height="215" VerticalAlignment="Stretch" ShowGridLines="False">
<Grid.Resources>
<c:People x:Key="MyFriends"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Left"
IsManipulationEnabled="True"
Height="20" Width="300">Activity Feed</TextBlock>
<ListBox Grid.Row="1" Name="listBox1" IsSynchronizedWithCurrentItem="True"
BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Auto"
VerticalContentAlignment="Stretch" Margin="0,0,0,5" Background="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Margin="5" BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding Path=Avatar}" Stretch="Fill" Width="50" Height="50" />
</Border>
<StackPanel Grid.Column="1" Margin="5">
<StackPanel Orientation="Horizontal" TextBlock.FontWeight="Bold" >
<TextBlock Text="{Binding Path=Firstname }" />
</StackPanel>
<TextBlock Text="{Binding Path=Comment}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
My Collection class is as following:
public class People : ObservableCollection<Person>
{ }
public class Person
{
private string firstName;
private string comment;
private Bitmap avatar;
public Person(string first, string comment, Bitmap avatar)
{
this.firstName = first;
this.comment = comment;
this.avatar = avatar;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string Comment
{
get { return comment; }
set { comment = value; }
}
public Bitmap Avatar
{
get { return avatar;}
set { avatar = value; }
}
public override string ToString()
{
return firstName.ToString();
}
}
Once my addin is loaded, I am downloading my data and setting the itemsSource.
People p = new People();
p.Add(new Person("Willa", "Some Comment", myAvatar));
p.Add(new Person("Isak", "Some Comment", myAvatar));
p.Add(new Person("Victor", "Some Comment", myAvatar));
this.wpfControl.listBox1.ItemsSource = p;
The problem I am facing is that the items are being rendered as empty rows whereas If I remove the dataTemplate, the items are rendered fine with their firstName.
Don't see anything wrong with the bindings themselves, but your avatar type seems off, WPF expects ImageSource (i do not know if there is any implicit convertion between Bitmap and ImageSource, check for binding errors to find out).

WPF popup: how to make a reusable template for popups?

Since Popup doesn't derive from Control and doesn't have a template, how can I define a template so that all popups look the same? I need to design one that has a certain look and don't want to have to copy markup each time one is used.
This seems pretty easy but I can't figure out how to do it. The Child property defines a logical tree but I don't see how you can pull that out into a template and reuse it.
I was looking to do the same thing and here is what I came up with:
I inherited from ContentPresenter, styled that control as I wanted and than placed the derived ContentPresenter inside my Popup, I only used 2 text blocks for the simplicity but it is easy to understand how any content could be added.
My custom control:
using System.Windows;
using System.Windows.Controls;
namespace CustomControls
{
[TemplatePart(Name = PART_PopupHeader, Type = typeof(TextBlock))]
[TemplatePart(Name = PART_PopupContent, Type = typeof(TextBlock))]
public class CustomPopupControl : ContentControl
{
private const string PART_PopupHeader = "PART_PopupHeader";
private const string PART_PopupContent = "PART_PopupContent";
private TextBlock _headerBlock = null;
private TextBlock _contentBlock = null;
static CustomPopupControl()
{
DefaultStyleKeyProperty.OverrideMetadata
(typeof(CustomPopupControl),
new FrameworkPropertyMetadata(typeof(CustomPopupControl)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_headerBlock = GetTemplateChild(PART_PopupHeader) as TextBlock;
_contentBlock = GetTemplateChild(PART_PopupContent) as TextBlock;
}
public static readonly DependencyProperty HeaderTextProperty =
DependencyProperty.Register("HeaderText", typeof(string), typeof(CustomPopupControl), new UIPropertyMetadata(string.Empty));
public string HeaderText
{
get
{
return (string)GetValue(HeaderTextProperty);
}
set
{
SetValue(HeaderTextProperty, value);
}
}
public static readonly DependencyProperty ContentTextProperty =
DependencyProperty.Register("ContentText", typeof(string), typeof(CustomPopupControl), new UIPropertyMetadata(string.Empty));
public string ContentText
{
get
{
return (string)GetValue(ContentTextProperty);
}
set
{
SetValue(ContentTextProperty, value);
}
}
}
}
Style for the control:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControls">
<Style TargetType="{x:Type local:CustomPopupControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomPopupControl}">
<Border CornerRadius="3" BorderThickness="1" BorderBrush="White">
<Border.Background>
<SolidColorBrush Color="#4b4b4b" Opacity="0.75"/>
</Border.Background>
<Border.Effect>
<DropShadowEffect ShadowDepth="0"
Color="White"
Opacity="1"
BlurRadius="5"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="{TemplateBinding HeaderText}"
Grid.Row="0"
Foreground="#5095d6"
FontWeight="Bold"
VerticalAlignment="Bottom"
Margin="{TemplateBinding Margin}"
HorizontalAlignment="Left"/>
<Rectangle Grid.Row="1" Stroke="AntiqueWhite" Margin="1 0"></Rectangle>
<TextBlock Grid.Row="2"
Grid.ColumnSpan="2"
x:Name="PART_TooltipContents"
Margin="5, 2"
Text="{TemplateBinding ContentText}"
TextWrapping="Wrap"
MaxWidth="200"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The use of the control:
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="Button1" Content="Button with popup" HorizontalAlignment="Center">
</Button>
<Button x:Name="Button2" Content="Another button with popup" HorizontalAlignment="Center">
</Button>
<Popup IsOpen="True"
FlowDirection="LeftToRight"
Margin="10"
PlacementTarget="{Binding ElementName=Button1}"
Placement="top"
StaysOpen="True">
<local2:CustomPopupControl HeaderText="Some Header Text" ContentText="Content Text that could be any text needed from a binding or other source" Margin="2">
</local2:CustomPopupControl>
</Popup>
<Popup IsOpen="True"
FlowDirection="LeftToRight"
Margin="10"
PlacementTarget="{Binding ElementName=Button2}"
Placement="Bottom"
StaysOpen="True">
<local2:CustomPopupControl HeaderText="Different header text" ContentText="Some other text" Margin="2">
</local2:CustomPopupControl>
</Popup>
</StackPanel>
I tried illustrating how some properties can be constant across all controls, others can be customized per control and others could be bound to TemplatePart, here is the final result:
Depends how you want your pop-ups to behave. If they're just for displaying information in a uniform manner, than you might want to have a class that derives from Window that has the standard formats and styling wrapped around a ContentPresenter then bind the content of the presenter to a property which can represent the custom information for each pop-up.
Then its just a matter of programatically inserting whatever custom content you want before displaying the pop-up window.
Hope it helps.

Resources