WPF - Custom design volume control - wpf

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

Related

How to set a property of a custom control from the main window?

I have a custom control, this is the generic.axml code:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Calendario"
xmlns:MyNamespace="clr-namespace:Calendario;assembly=Calendario"
xmlns:Converters="clr-namespace:Calendario.Converters">
<Converters:DateConverter x:Key="DateConverter"></Converters:DateConverter>
<Converters:DayBorderColorConverter x:Key="DayBorderColorConverter"></Converters:DayBorderColorConverter>
<Style TargetType="{x:Type local:CalendarioPersonalizado}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CalendarioPersonalizado}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<TextBlock Text="{Binding Date}" />
<Grid Height="30" DockPanel.Dock="Top">
</Grid>
<ItemsControl ItemsSource="{Binding Days}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="{Binding ColorRecuadroExterno, Mode=TwoWay}" BorderThickness="1" Padding="0">
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And I have my Calendario.cs with the dpendency property:
public static readonly DependencyProperty ColorRecuadroExternoProperty = DependencyProperty.Register("ColorRecuadroExterno", typeof(Brush), typeof(CalendarioPersonalizado));
public Brush ColorRecuadroExterno
{
get { return (Brush)GetValue(ColorRecuadroExternoProperty); }
set { SetValue(ColorRecuadroExternoProperty, value); }
}
And later in my main windows I use the control:
<local:CalendarioPersonalizado x:Name="cCalendario" ColorRecuadroExterno="Green"/>
The problem is that the border of the day in the calendar is not set to green like I have tried to set in the main window.
Also in the code behid I have tried this:
cCalendario.ColorRecuadroExterno = System.Windows.Media.Brushes.Green;
But the the color is not set.
What I want to do is set the color of the border in my custom cotrol from my main application.
Thanks.
If you put a Callback method in your local:CalendarioPersonalizado class and set your backround in this callback method. I think it is going to work.
public static readonly DependencyProperty ColorRecuadroExternoProperty = DependencyProperty.Register("ColorRecuadroExterno", typeof(Brush), typeof(CalendarioPersonalizado),
new PropertyMetadata(Brushes.Brown, Callback));
private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CalendarioPersonalizado obj = d as CalendarioPersonalizado;
obj.ColorRecuadroExterno.Background = (Brush)e.NewValue;
}
Your DependencyProperty is of type Brush. You can't implicitly convert "Green" to a Brush.
You either need to use a converter to convert a string representation of a color to a brush, or make your property type Color, and bind it to an appropriate property.

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 Change a property in a customcontrol after a button click

I posted a question in this link. maybe I'm not well expressed.
It's very simple, I want to change a property in a usercontrol or CustomControl after a click on a Boutton outside...
The code of the customcontrol is as follows :
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border x:Name="container" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Hidden" Value="true">
<Setter Property="BorderBrush" Value="Blue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public class CustomControl1 : Control
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public bool Hidden
{
get { return (bool)GetValue(HiddenProperty); }
set { SetValue(HiddenProperty, value); }
}
// Using a DependencyProperty as the backing store for Hidder. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HiddenProperty =
DependencyProperty.Register("Hidden", typeof(bool), typeof(CustomControl1), new PropertyMetadata(false));
}
And a simple window for test
<Window x:Class="WpfTestCustomControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomBorder;assembly=WpfCustomBorder"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
<ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<local:CustomControl1 x:Name="cc" BorderBrush="Red" BorderThickness="3" Margin="10" Grid.RowSpan="2"/>
<Button Grid.Column="1" Content="Ok" Margin="5" Click="Button_Click"/>
</Grid>
namespace WpfTestCustomControl
{
/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
cc.Hidden = true;
}
}
}
The property "Hidden" is a dependency property inside the custom control.
When i click on the button in mainwindow i want to change the hidden property to true. this must fire the trigger inside the custom control to change borderbrush to "blue" color. While nothing happen.
Is there something missing or is not the right way to do it ?
Thanks in advance..
Don't hard-set BorderBrush="Red" in your Control's declaration, it's prioritary over any trigger's setter.
You might want to check msdn's Dependency Property Value Precedence

capture events from lookless control button

I have create a lookless control to be used in a Silverlight 4 project. This control contains a button and I would like to capture the click event. The Generic.xaml contains
<Style TargetType="TU:MyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TU:MyControl" >
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" d:DesignWidth="550" d:DesignHeight="228">
<Grid Background="Silver">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150*"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="150*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Margin="2" BorderBrush="DarkGray" BorderThickness="3"></Border>
<Border Grid.Column="2" Margin="2" BorderBrush="DarkGray" BorderThickness="3"></Border>
<StackPanel Grid.Column="1">
<Button Name="PART_MyClick" Height="32" Width="32" Margin="0,8,0,0"></Button>
</StackPanel>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
After researching the problem I beleive that I have to add the following attribute to my control class
[TemplatePart(Name = "PART_MyClick", Type = typeof(Button))]
Then in my controls constructor I have added the following code
var myClick = GetTemplateChild("PART_MyClick") as Button;
if(myClick != null)
{
myClick.Click += (o, e) => DoThings();
}
when run though the myClick variable is always null so the event handler never gets attached. Could you please tell me where I am going wrong? Im a newbie so if this is the wrong approach completly then any advise on the correct approach would also be greatfully received
Override the OnApplyTemplate method and put your code there instead of the control's constructor:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var myClick = GetTemplateChild("PART_MyClick") as Button;
if(myClick != null)
{
myClick.Click += (o, e) => DoThings();
}
}
Because during the constructor call the visual tree for the control is not build up yet. From MSDN OnApplyTemplate:
Attach class-defined event handlers to parts of the template. For
example, you might want class logic to handle KeyDown events from a
TextBox template part so that UI states are updated, and other events
that are specific to your control are raised instead.

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