Xaml name scope - wpf

I need to change Rectangle attribute from C# (his RotateTransform angle)
The problem is the rectangle was declared in XAML, and in the C# code is out of scope for the rectangle, I tried to use Name, and X:Name without succeed,
How should I do it?
-------------Edit--------------------
<ContentControl Width="5" Height="400" Canvas.Top="80" Canvas.Left="350"
Template="{StaticResource DesignerItemTemplateLine}">
<Rectangle Fill="Blue" IsHitTestVisible="False" Name="mRect">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="45"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</ContentControl>
C# code
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control designerItem = this.DataContext as Control;
if (designerItem != null)
{
double left = Canvas.GetLeft(designerItem);
double top = Canvas.GetTop(designerItem);
if (mRect != null)// This line don't compile
Canvas.SetLeft(designerItem, left + e.HorizontalChange);
Canvas.SetTop(designerItem, top + e.HorizontalChange);
//Canvas.SetTop(designerItem, top + e.VerticalChange);
}
}
You can notice that "if (mRect != null)" does not pass compilation
-------------Seconde Edit--- All the code----------------------------
<Window x:Class="DiagramDesigner.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:DiagramDesigner"
WindowStartupLocation="CenterScreen"
Title="Move"
Height="550" Width="750">
<!-- MoveThumb Template -->
<ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type s:MoveThumbUpDwon}">
<Rectangle Fill="Transparent"/>
</ControlTemplate>
<!-- MoveThumb Template -->
<ControlTemplate x:Key="MoveThumbTemplateLeftRight" TargetType="{x:Type s:MoveThumbLeftRight}">
<Rectangle Fill="Transparent"/>
</ControlTemplate>
<!-- MoveThumb Template -->
<ControlTemplate x:Key="MoveLineTemplate" TargetType="{x:Type s:MoveLine}">
<Rectangle Fill="Black">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="45"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</ControlTemplate>
<!-- Designer Item Template-->
<ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<s:MoveThumbUpDwon Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="DesignerItemTemplateLeftRight" TargetType="ContentControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<s:MoveThumbLeftRight Template="{StaticResource MoveThumbTemplateLeftRight}" Cursor="SizeAll"/>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="DesignerItemTemplateLine" TargetType="ContentControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<s:MoveLine Template="{StaticResource MoveLineTemplate}" Cursor="SizeAll"/>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
</Grid>
</ControlTemplate>
</Window.Resources>
<Canvas>
<ContentControl Width="600"
Height="5"
Canvas.Top="250"
Canvas.Left="80"
Template="{StaticResource DesignerItemTemplate}">
<Rectangle Fill="Blue"
IsHitTestVisible="False"/>
</ContentControl>
<ContentControl Width="5"
Height="400"
Canvas.Top="80"
Canvas.Left="350"
Template="{StaticResource DesignerItemTemplateLeftRight}">
<Rectangle Fill="Blue"
IsHitTestVisible="False"/>
</ContentControl>
<ContentControl Width="5" Height="400" Canvas.Top="80" Canvas.Left="350"
Template="{StaticResource DesignerItemTemplateLine}">
<Rectangle Fill="Blue" IsHitTestVisible="False" Name="mRect">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="45"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</ContentControl>
</Canvas>
Now the C# code
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows;
namespace DiagramDesigner
{
public class MoveThumbUpDwon : Thumb
{
public MoveThumbUpDwon()
{
DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta);
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control designerItem = this.DataContext as Control;
if (designerItem != null)
{
double left = Canvas.GetLeft(designerItem);
double top = Canvas.GetTop(designerItem);
//Canvas.SetLeft(designerItem, left + e.HorizontalChange);
Canvas.SetTop(designerItem, top + e.VerticalChange);
}
}
}
public class MoveThumbLeftRight : Thumb
{
public MoveThumbLeftRight()
{
DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta);
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control designerItem = this.DataContext as Control;
if (designerItem != null)
{
double left = Canvas.GetLeft(designerItem);
double top = Canvas.GetTop(designerItem);
Canvas.SetLeft(designerItem, left + e.HorizontalChange);
//Canvas.SetTop(designerItem, top + e.VerticalChange);
}
}
}
public class MoveLine : Thumb
{
public MoveLine()
{
DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta);
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control designerItem = this.DataContext as Control;
if (designerItem != null)
{
double left = Canvas.GetLeft(designerItem);
double top = Canvas.GetTop(designerItem);
// if (mRect != null)
Canvas.SetLeft(designerItem, left + e.HorizontalChange);
Canvas.SetTop(designerItem, top + e.HorizontalChange);
//Canvas.SetTop(designerItem, top + e.VerticalChange);
}
}
}
}

The reference is in the code-behind, but it seems to be in a different class than the xaml:
<Window x:Class="DiagramDesigner.Window1"> vs public class MoveLine : Thumb
They must be the same class if you are going to reference xaml-defined elements from the code-behind.

It looks like your window's Canvas contains a number of ContentControls. One of these is of interest and contains a grid which in turn contains firstly an instance of your MoveLine class, and secondly a ContentPresenter containing a Rectangle.
So your visual tree looks roughly like this:
Window1
Canvas
...
ContentControl
Grid
MoveLine
ContentPresenter
Rectangle (mRect)
You're trying to handle an event in MoveLine and modify the ContentPresenter's Rectangle, mRect. You can refer to mRect only in the context of Window1.
The problem is that as far as WPF is concerned the MoveLine class could appear anywhere, and so naturally it has no idea what mRect might mean to any particular MoveLine instance. As it happens we know that mRect is the child Rectangle of a sibling ContentPresenter which shares a parent with an instance of MoveLine.
If you're absolutely sure that MoveLine will only ever be used here, you could use System.Windows.Media.VisualTreeHelper's GetParent(), GetChildrenCount() and GetChild() methods. You need to go "up" one level from MoveLine, across one, and then down one. You could also go up one level and then search for descendants with the name "mName". See How can I find WPF controls by name or type?. This isn't a very sane approach though.
I'd be more tempted to put the handling code onto the canvas, or at least into its ContentControls, since they're the ones being affected. I would add a RoutedEvent called, say, ThumbMoved, to MoveLine. RoutedEvents can "bubble up" to be handled by ancestral controls. You can then add a handler for this event to the ContentControl containing your Rectangle, and it can then use mName to adjust the rectangle. See How to: Create a Custom Routed Event and How to: Handle a Routed Event. Very roughly:
class MoveLine : Thumb
{
public static readonly RoutedEvent ThumbMovedEvent =
EventManager.RegisterRoutedEvent("ThumbMoved",
RoutingStrategy.Bubble,
typeof(DragDeltaRoutedEventHandler),
typeof(MoveLine));
public event DragDeltaRoutedEventHandler ThumbMoved
{
add { AddHandler(ThumbMovedEvent, value); }
remove { RemoveHandler(ThumbMovedEvent, value); }
}
void RaiseThumbMoved(DragDeltaEventArgs e)
{
DragDeltaRoutedEventArgs newEventArgs =
new DragDeltaRoutedEventArgs(ThumbMovedEvent, e);
RaiseEvent(newEventArgs);
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
RaiseThumbMoved(e);
...
}
(Where DragDeltaRoutedEventArgs is a class derived from RoutedEventArgs which provides the same deltas as DragDeltaEventArgs.) And then in your window's XAML:
...
<ContentControl Width="5" Height="400" Canvas.Top="80" Canvas.Left="350"
Template="{StaticResource DesignerItemTemplateLine}"
MoveLine.ThumbMoved="MoveLine_ThumbMoved">
<Rectangle Fill="Blue" IsHitTestVisible="False" Name="mRect">
...
And code behind:
namespace DiagramDesigner
{
public partial class Window1 : Window
{
private void MoveLine_ThumbMoved(object sender, DragDeltaRoutedEventArgs e)
{
mRect.Foo = "Bar"; // mRect is recognised by Window1.
}
...

Related

DragMove() will make the Border with cornerRadius lose its mouseover trigger state?

I have created a borderless window with rounded corners, and added the drag event and a trigger to it. Here is the simple code:
<Window x:Class="DebugTest.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:DebugTest"
mc:Ignorable="d" Height="200" Width="200"
AllowsTransparency="True" WindowStyle="None" Background="Transparent">
<Border x:Name="MainBorder" CornerRadius="15" Background="White" BorderBrush="Black" BorderThickness="1">
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MainBorder,Path=IsMouseOver}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Button Content="x" HorizontalAlignment="Right" VerticalAlignment="Top"
Margin="5" Height="20" Width="20" Click="Button_Click"/>
</Grid>
</Border>
</Window>
public MainWindow()
{
InitializeComponent();
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
this.DragMove();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
But when I run the exe file, click on the blank area within the window, the button will appear very obvious flickering situation.
Strangely enough, this situation hardly occurs when debugging in Visual Studio instead of double click the file, and it also doesn't happen while CornerRadius="0".
It looks like it lost the mouseover trigger on click, but I can't think of any good way to avoid flicker appearing, and to satisfy the need for both with rounded corners, draggable, and with trigger.
Well, although I don't know why only rounded corners would cause DragMove() to trigger the MouseLeave event, I circumvented this with background code instead of using xaml trigger.
<Border x:Name="MainBorder" CornerRadius="15" Background="White"
BorderBrush="Black" BorderThickness="1"
MouseEnter="MainBorder_MouseEnter" MouseLeave="MainBorder_MouseLeave">
<Grid Visibility="Hidden" x:Name="TriggerBorder">
<Button Content="x" HorizontalAlignment="Right" VerticalAlignment="Top"
Margin="5" Height="20" Width="20" Click="Button_Click"/>
</Grid>
</Border>
bool dragMoving = false;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
dragMoving = true;
this.DragMove();
dragMoving = false;
}
private void MainBorder_MouseEnter(object sender, MouseEventArgs e)
{
TriggerBorder.Visibility = Visibility.Visible;
}
private void MainBorder_MouseLeave(object sender, MouseEventArgs e)
{
if (dragMoving) return;
TriggerBorder.Visibility = Visibility.Hidden;
}
Seems to work fine.

WPF why my ObservableCollection object collection not update my ProgressBar

i have WPF application and i am using PcapDotNet DLLs to send packets through the Networkcard.
This is my object that represent Wireshark file:
public class WiresharkFile
{
public event PropertyChangedEventHandler PropertyChanged;
virtual public void NotifyPropertyChange(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
private string _file; // file path
private string _packets; // how many packet in file
private string _sentPackets; // how many packet sent
private string _progress; // percentage (_sentPackets/_packets) * 100
public int Progress
{
get { return _progress; }
set
{
_progress = value;
NotifyPropertyChange("Progress");
}
}
public void Transmit(WireshrkFile)
{
// here i am send the packets
}
}
My list that hold this objects:
public ObservableCollection<WireshrkFile> files{ get; set; }
As you can see in every file that in process i am calculate the Progress.
Now all my files is inside ListView and in this ListView i have Progress-Bar column.
This is my Progress-Bar style:
<!-- Progress-Bar style -->
<Style x:Key="CustomProgressBar" TargetType="ProgressBar" >
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Border BorderBrush="Transparent" BorderThickness="1" Background="LightGray" CornerRadius="0" Padding="0" >
<Grid x:Name="PART_Track">
<Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left" Fill="#FF15669E" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Progress-Bar definition inside my ListView
<ListView.Resources>
<DataTemplate x:Key="MyDataTemplate">
<Grid Margin="-6">
<ProgressBar Name="prog" Maximum="100" Value="{Binding Progress}"
Width="{Binding Path=Width, ElementName=ProgressCell}"
Height="20" Margin="0" Background="#FFD3D0D0" Style="{StaticResource CustomProgressBar}" />
<TextBlock Text="{Binding Path=Value, ElementName=prog, StringFormat={}{0}%}" VerticalAlignment="Center"
HorizontalAlignment="Center" FontSize="11" Foreground="Black" />
</Grid>
</DataTemplate>
<ControlTemplate x:Key="ProgressBarTemplate">
<Label HorizontalAlignment="Center" VerticalAlignment="Center" />
</ControlTemplate>
</ListView.Resources>
So when the Wireshark file is in process i can see that it send the packets and the properties changing but the Propress-Bar is still with 0%.
So i made i little test and inside timer event click changed my WireshakFile Progress property and in this case i can see the my ListView Progress-Bar changing.
So my question is what i am doing wrong ?
Edit:
I also try something that looks very strange but this is woking:
Inside my Timer_Tick i try to loop over my ObservableCollection collection:
foreach (WiresharkFile item in files)
{
item.Progress = item.Progress;
}
As you can see it only make pointer to the same property but this is working.

WPF - Custom design volume control

I have been working with WPF for some time.
I need to create the following control over Internet, but could not find appropriate.
Can anybody help how to implement this functionality. Value should be increasing or decreasing when clicked on control.
I found that I can use either Volume control or Slider, but not getting clear what I should use.
Thanks in anticipation.
I prefer to use a Progressbar for these kind of displays.
This is my implementation of a simple volume control looking pretty much like the one you show as an example:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private double _volume;
private bool mouseCaptured = false;
public double Volume
{
get { return _volume; }
set
{
_volume = value;
OnPropertyChanged("Volume");
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void MouseMove(object sender, MouseEventArgs e)
{
if (Mouse.LeftButton == MouseButtonState.Pressed && mouseCaptured)
{
var x = e.GetPosition(volumeBar).X;
var ratio = x/volumeBar.ActualWidth;
Volume = ratio*volumeBar.Maximum;
}
}
private void MouseDown(object sender, MouseButtonEventArgs e)
{
mouseCaptured = true;
var x = e.GetPosition(volumeBar).X;
var ratio = x / volumeBar.ActualWidth;
Volume = ratio * volumeBar.Maximum;
}
private void MouseUp(object sender, MouseButtonEventArgs e)
{
mouseCaptured = false;
}
#region Property Changed
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
And the XAML:
<Window x:Class="VolumeControlApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="196" Width="319">
<Window.Resources>
<Style x:Key="VolumeStyle" TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="#FFB00606"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid x:Name="TemplateRoot">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
<Rectangle x:Name="PART_Track"/>
<Grid x:Name="PART_Indicator" ClipToBounds="True" HorizontalAlignment="Left">
<Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}" RadiusX="5" RadiusY="3"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Background="#FF363636">
<Border Background="Gray" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center" CornerRadius="3" Padding="2">
<Border Background="Black" CornerRadius="3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Vol:" Foreground="White" VerticalAlignment="Center" Margin="4 0"/>
<ProgressBar x:Name="volumeBar" Height="10"
Value="{Binding Volume}"
Width="100"
MouseMove="MouseMove"
MouseDown="MouseDown"
MouseUp="MouseUp" Style="{DynamicResource VolumeStyle}" Grid.Column="1"/>
</Grid>
</Border>
</Border>
</Grid>
</Window>
You could use a slider and create a template for it.
If you need special mouse handling you'll need to subclass the slider and add logic/event handling.
The standard Slider template has a couple of repeat buttons. By simply making the left repeat button red you have a very basic implementation of the required control.
Take a look at this posts hope it helps you..
Link:
1: Sliders
2: Similar to VLC player volume control

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.

WPF Adding a Tooltip to the Track of a Slider

I have added a tooltip (shown below) to the track in the slider template, but rather than binding to the current value of the slider, I would like to bind to the value corresponding to the "track value" the mouse is over. Similar to what the youtube video slider allows. So the user can mouseover the track and also see the corresponding value, without having to actually move the thumb.
<Track Grid.Row="1" Name="PART_Track" ToolTip="{Binding Path=Value}" ToolTipService.Placement="Mouse">
</Track>
Any ideas? Thanks!
I have created an attached behaviour that will find the Track from a slider and subscribe to its MouseMove event to set the tooltip of the track to the corresponding value of the tick the mouse is over. I also added a Prefix property so you can write what the value is:
internal class ShowTickValueBehavior : Behavior<Slider>
{
private Track track;
public static readonly DependencyProperty PrefixProperty = DependencyProperty.Register(
"Prefix",
typeof(string),
typeof(ShowTickValueBehavior),
new PropertyMetadata(default(string)));
public string Prefix
{
get
{
return (string)this.GetValue(PrefixProperty);
}
set
{
this.SetValue(PrefixProperty, value);
}
}
protected override void OnAttached()
{
this.AssociatedObject.Loaded += this.AssociatedObjectOnLoaded;
base.OnAttached();
}
protected override void OnDetaching()
{
this.track.MouseMove -= this.TrackOnMouseMove;
this.track = null;
base.OnDetaching();
}
private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
this.AssociatedObject.Loaded -= this.AssociatedObjectOnLoaded;
this.track = (Track)this.AssociatedObject.Template.FindName("PART_Track", this.AssociatedObject);
this.track.MouseMove += this.TrackOnMouseMove;
}
private void TrackOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
var position = mouseEventArgs.GetPosition(this.track);
var valueFromPoint = this.track.ValueFromPoint(position);
var floorOfValueFromPoint = (int)Math.Floor(valueFromPoint);
var toolTip = string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.Prefix, floorOfValueFromPoint);
ToolTipService.SetToolTip(this.track, toolTip);
}
}
Usage
<Window x:Class="TestSlider.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-TestSlider"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
>
<Grid>
<Slider Name="Slider1"
IsSnapToTickEnabled="True"
TickFrequency="1"
TickPlacement="BottomRight"
IsMoveToPointEnabled="True"
Minimum="13"
Maximum="25"
>
<i:Interaction.Behaviors>
<local:ShowTickValueBehavior Prefix="Volume: "/>
</i:Interaction.Behaviors>
</Slider>
</Grid>
Result:
I'd imagine you're going to have to create a new control, inheriting from the slider. You'd need to implement mousein/out and mousemove, calculate the value based on mouse offset and change the tooltip.
I don't think there's any property you can use "out of the box" so the calculation may be rather tricky if you need to factor in reskinning of margins etc.
First you need modify Slider control
<Slider VerticalAlignment="Center" HorizontalAlignment="Stretch">
<Slider.Template>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<xamlTest:ReflectablePopup x:Name="InfoPopup" Width="Auto" Height="Auto" PlacementTarget="{Binding ElementName=Thumb}" Placement="Top" StaysOpen="False" IsOpen="False" AllowsTransparency="True">
<Border Padding="2" CornerRadius="3" Background="#555C5C5C">
<Label Content="Your Text"></Label>
</Border>
</xamlTest:ReflectablePopup>
<Track x:Name="PART_Track">
<Track.Thumb>
<Thumb x:Name="Thumb" Width="10" Height="20">
</Thumb>
</Track.Thumb>
</Track>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="Thumb" Property="IsDragging" Value="True">
<Setter Value="True" TargetName="InfoPopup" Property="IsOpen" />
</Trigger>
<Trigger SourceName="Thumb" Property="IsDragging" Value="False">
<Setter Value="False" TargetName="InfoPopup" Property="IsOpen" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Slider.Template>
</Slider>
As you see, I using ReflectablePopup instead Popup. Because Popup can`t relocate after PlacementTarget moved.
Below the code fo ReflectablePopup (C#):
public class ReflectablePopup : Popup
{
protected override void OnOpened(EventArgs e)
{
var friend = this.PlacementTarget;
friend.QueryCursor += friend_QueryCursor;
base.OnOpened(e);
}
protected override void OnClosed(EventArgs e)
{
var friend = this.PlacementTarget;
friend.QueryCursor -= friend_QueryCursor;
base.OnClosed(e);
}
private void friend_QueryCursor(object sender, System.Windows.Input.QueryCursorEventArgs e)
{
this.HorizontalOffset += +0.1;
this.HorizontalOffset += -0.1;
}
}

Resources