Stylizing graphs and tooltip of DataVisualization.Chart - c#

I'm creating a UserControl component of type DataVisualization.Chart. I'm building component based on the example of url: http://www.dotnetcurry.com/ShowArticle.aspx?ID=553. This chart will generate charts of the type chosen as either columns, bars, lines, Pie ... In the construction of the component, i'm inserting the Data Source than is passed through another View. So far so good. The chart for all types are generated normally.
What I need to do now are 3 modifications in the component. But I can't find a way to resolve these modifications:
When the user places the mouse over the value generated by the graph, the ToolTip appears the value of the object .. What I need is that the ToolTip Text appear as IndependentValuePath, which would be the name + value in the following format: "Name (value)";
When a graph is generated, it inserts as a kind of label depending on the graph is the x axis or the y axis (as pictured). I need to remove it (Example in image);
For graphs of type Column (and probably others yet to be confirmed), the outline of the rectangle must be the same color as the internal color. Besides being in the same color, each rectangle, which will change color according to a pre-defined range, which will be passed as the DataSource of the previous View .. What would be this: if the value is below 200, the color is red, if between 200 and 350, will be yellow ... and so on. I'll have about 5 limits;
PS: the last THIS is the tooltip.
Does anyone know how I can stylize these graphs and tooltip?
Best regards,
Gustavo
Edit: Here's my UserControl Chart:
<UserControl x:Class="Library.Core.GUI.WPF.Controls.ChartUCControl"
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:ct="clr-namespace:Library.Core.GUI.WPF.Controls"
xmlns:chart="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
x:Name="ChartUCControl1">
<UserControl.Resources>
<Style TargetType="chart:ColumnDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type chart:ColumnDataPoint}">
<!--<Border BorderThickness="0" Background="Red" ToolTip="asdfasdf"/>-->
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Width="auto"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" >
<chart:Chart Name="chartView" Grid.Row="0" Title="{Binding TitleGraphic, Mode=TwoWay, ElementName=ChartUCControl1, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.IsDeferredScrollingEnabled="True"
Width="{Binding WidthScrollViewer, Mode=TwoWay, ElementName=ChartUCControl1, UpdateSourceTrigger=PropertyChanged}"
MinWidth="{Binding MinWidthScrollViewer, Mode=TwoWay, ElementName=ChartUCControl1, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="{Binding HorizontalAlignmentGraphic, Mode=TwoWay, ElementName=ChartUCControl1, UpdateSourceTrigger=PropertyChanged}" >
<chart:Chart.Series>
<chart:ColumnSeries >
<ToolTipService.ToolTip>
<ContentControl Content="asdçlfkj"/>
</ToolTipService.ToolTip>
<chart:ColumnSeries.IndependentAxis>
<chart:CategoryAxis Orientation="X" Visibility="Visible" Height="0"/>
</chart:ColumnSeries.IndependentAxis>
</chart:ColumnSeries>
</chart:Chart.Series>
<!--<ToolTipService.ToolTip>
<ContentControl Content="{TemplateBinding IndependentValue}" />
</ToolTipService.ToolTip>-->
<!--PreviewMouseMove="chartView_PreviewMouseMove"-->
<!--<chart:Chart.Series>
<chart:ColumnSeries>
<chart:DataPointSeries.DataPointStyle>
<Style TargetType="chart:ColumnDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type chart:ColumnDataPoint}">
<Border BorderBrush="Black" >
<ToolTipService.ToolTip>
<ContentControl Content="{Binding ToolTip}"/>
</ToolTipService.ToolTip>
<Border BorderBrush="Black" BorderThickness="1"/>
<ToolTipService.ToolTip>
<ContentControl Content="asçldfkj"/>
</ToolTipService.ToolTip>
</Border>
<ToolTip Content="çlkjasf"/>
<Rectangle>
<Rectangle.Fill>
<SolidColorBrush Color="Blue"/>
</Rectangle.Fill>
</Rectangle>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</chart:DataPointSeries.DataPointStyle>
</chart:ColumnSeries>
</chart:Chart.Series>-->
<!--<Style TargetType="{x:Type chart:ColumnDataPoint}">
<Setter Property="Background" Value="Orange" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type chart:ColumnSeries}">
<Border BorderBrush="Azure" BorderThickness="10" Opacity="10" x:Name="Root">
<Grid Background="Black">
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="Black" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Border BorderBrush="#ccffffff" BorderThickness="1">
<Border BorderBrush="#77ffffff" BorderThickness="1" />
</Border>
<Rectangle x:Name="SelectionHighlight" Fill="Red" Opacity="0" />
<Rectangle x:Name="MouseOverHighlight" Fill="White" Opacity="0" />
</Grid>
<ToolTipService.ToolTip>
<ContentControl Content="as" />
</ToolTipService.ToolTip>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>-->
<!--<ToolTipService.ToolTip>
<ContentControl Content="{Binding Identificacao}"/>
</ToolTipService.ToolTip>-->
<!--<Grid>
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="Blue" Offset="0"/>
<GradientStop Color="Black" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>-->
<chart:Chart.Axes>
<chart:LinearAxis Orientation="X" Interval="{Binding IntervalAxis, ElementName=ChartUCControl1, Mode=TwoWay}"
MouseEnter="chartView_PreviewMouseMove"
Maximum="{Binding MaximumAxis, ElementName=ChartUCControl1, Mode=TwoWay}"/>
<!-- This section configures labels of X Orientation -->
<chart:CategoryAxis Orientation="X" Visibility="Hidden"/>
</chart:Chart.Axes>
<chart:Chart.LegendStyle>
<Style TargetType="Control">
<Setter Property="Width" Value="0"/>
<Setter Property="Height" Value="0"/>
</Style>
</chart:Chart.LegendStyle>
<chart:Chart.PlotAreaStyle>
<Style TargetType="Grid">
<Setter Property="Background" Value="Transparent"/>
<!--<Setter Property="ToolTip" Value="{x:Null}"/>-->
</Style>
</chart:Chart.PlotAreaStyle>
</chart:Chart>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>
IN case of need to look my code-behind:
namespace Library.Core.GUI.WPF.Controls
{
public partial class ChartUCControl : UserControl, INotifyPropertyChanged
{
#region Constructors
public ChartUCControl()
{
InitializeComponent();
}
#endregion
#region ChartType Property
public static ChartTypes GetChartType(DependencyObject d)
{
return (ChartTypes)d.GetValue(ChartTypeProperty);
}
public static void SetChartType(DependencyObject d, ChartTypes value)
{
d.SetValue(ChartTypeProperty, value);
}
public static readonly DependencyProperty ChartTypeProperty =
DependencyProperty.RegisterAttached(
"ChartType",
typeof(ChartTypes),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(ChartTypeChangedCallback)
);
private static void ChartTypeChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Chart instance = (d as Chart);
if (instance.IsNull())
return;
SetChartType(d, (ChartTypes)e.NewValue);
ChartUCControl instance2 = (d as ChartUCControl);
}
private ChartTypes chartType;
public ChartTypes ChartType
{
get { return chartType; }
set { chartType = value; }
}
#endregion
#region ChartAreaSelected Property
public static readonly DependencyProperty ChartAreaProperty =
DependencyProperty.RegisterAttached(
"ChartArea",
typeof(Boolean),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(ChartAreaChangedCallback));
private static void ChartAreaChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ChartUCControl instance = (d as ChartUCControl);
if (instance.IsNull())
return;
instance.ChartArea = (Boolean)e.NewValue;
}
private Boolean chartArea;
public Boolean ChartArea
{
get { return chartArea; }
set
{
chartArea = value;
if (value)
{
cmbChart.SelectedItem = ChartTypes.Area;
DoRenderizeGraphic();
}
}
}
#endregion
#region ChartBarSelected Property
public static readonly DependencyProperty ChartBarProperty =
DependencyProperty.RegisterAttached(
"ChartBar",
typeof(Boolean),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(ChartBarChangedCallback));
private static void ChartBarChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ChartUCControl instance = (d as ChartUCControl);
if (instance.IsNull())
return;
instance.ChartBar = (Boolean)e.NewValue;
}
private Boolean chartBar;
public Boolean ChartBar
{
get { return chartBar; }
set
{
chartBar = value;
if (value)
{
cmbChart.SelectedItem = ChartTypes.Bar;
DoRenderizeGraphic();
}
}
}
#endregion
#region ChartColumnSelected Property
public static readonly DependencyProperty ChartColumnProperty =
DependencyProperty.RegisterAttached(
"ChartColumn",
typeof(Boolean),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(ChartColumnChangedCallback)
);
private static void ChartColumnChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ChartUCControl instance = (d as ChartUCControl);
if (instance.IsNull())
return;
instance.ChartColumn = (Boolean)e.NewValue;
}
private Boolean chartColumn;
public Boolean ChartColumn
{
get { return chartColumn; }
set
{
chartColumn = value;
if (value)
{
cmbChart.SelectedItem = ChartTypes.Columns;
DoRenderizeGraphic();
}
}
}
#endregion
#region ChartLinesSelected Property
public static readonly DependencyProperty ChartLinesProperty =
DependencyProperty.RegisterAttached(
"ChartLines",
typeof(Boolean),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(ChartLinesChangedCallback));
private static void ChartLinesChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ChartUCControl instance = (d as ChartUCControl);
if (instance.IsNull())
return;
instance.ChartLines = (Boolean)e.NewValue;
}
private Boolean chartLines;
public Boolean ChartLines
{
get { return chartLines; }
set
{
chartLines = value;
if (value)
{
cmbChart.SelectedItem = ChartTypes.Lines;
DoRenderizeGraphic();
}
}
}
#endregion
#region FieldForIndependentValue Property
/// <summary>
/// String containing list of the only characters allowed, others will be filtered
/// </summary>
public String FieldForIndependentValue
{
get { return (String)GetValue(FieldForIndependentValueProperty); }
set { SetValue(FieldForIndependentValueProperty, value); }
}
public static readonly DependencyProperty FieldForIndependentValueProperty =
DependencyProperty.Register("FieldForIndependentValue", typeof(String), typeof(ChartUCControl));
#endregion
#region FieldForDependentValue Property
/// <summary>
/// String containing list of the only characters allowed, others will be filtered
/// </summary>
public String FieldForDependentValue
{
get { return (String)GetValue(FieldForDependentValueProperty); }
set { SetValue(FieldForDependentValueProperty, value); }
}
public static readonly DependencyProperty FieldForDependentValueProperty =
DependencyProperty.Register("FieldForDependentValue", typeof(String), typeof(ChartUCControl));
#endregion
#region TitleGraphic Property
public String TitleGraphic
{
get { return (String)base.GetValue(TitleGraphicProperty); }
set { base.SetValue(TitleGraphicProperty, value); }
}
public static readonly DependencyProperty TitleGraphicProperty =
DependencyProperty.RegisterAttached(
"TitleGraphic",
typeof(String),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(null)
);
#endregion
#region MinWidthScrollViewer Property
public String MinWidthScrollViewer
{
get { return (String)base.GetValue(MinWidthScrollViewerProperty); }
set { base.SetValue(MinWidthScrollViewerProperty, value); }
}
public static readonly DependencyProperty MinWidthScrollViewerProperty =
DependencyProperty.RegisterAttached(
"MinWidthScrollViewer",
typeof(String),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(null));
#endregion
#region Width ScrollViewer Property
public String WidthScrollViewer
{
get { return (String)base.GetValue(WidthScrollViewerProperty); }
set { base.SetValue(WidthScrollViewerProperty, value); }
}
public static readonly DependencyProperty WidthScrollViewerProperty =
DependencyProperty.RegisterAttached(
"WidthScrollViewer",
typeof(String),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(null)
);
#endregion
#region ItemsSource Property
public IEnumerable ItemsSource
{
get { return (IEnumerable)base.GetValue(ItemsSourceProperty); }
set { base.SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.RegisterAttached(
"ItemsSource",
typeof(IEnumerable),
typeof(ChartUCControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
#endregion
#region Methods
private void ImageButton_Click_1(object sender, RoutedEventArgs e)
{
DoRenderizeGraphic();
}
public void DoRenderizeGraphic()
{
while (this.chartView.Series.Count() - 1 >= 0)
this.chartView.Series.Remove(this.chartView.Series[0]);
DataPointSeries objChar = null;
if (!cmbChart.SelectedItem.IsNull())
switch ((ChartTypes)cmbChart.SelectedValue)
{
case ChartTypes.Bar:
this.chartView.Series.Add(new BarSeries());
objChar = this.chartView.Series[0] as BarSeries;
break;
case ChartTypes.Columns:
this.chartView.Series.Add(new ColumnSeries());
objChar = this.chartView.Series[0] as ColumnSeries;
break;
case ChartTypes.Pie:
this.chartView.Series.Add(new PieSeries());
objChar = this.chartView.Series[0] as PieSeries;
break;
case ChartTypes.Lines:
this.chartView.Series.Add(new LineSeries());
objChar = this.chartView.Series[0] as LineSeries;
break;
case ChartTypes.Area:
this.chartView.Series.Add(new AreaSeries());
objChar = this.chartView.Series[0] as AreaSeries;
break;
default:
break;
}
if (!objChar.IsNull())
{
objChar.IsSelectionEnabled = true;
objChar.DependentValuePath = FieldForDependentValue;
objChar.IndependentValuePath = FieldForIndependentValue;
objChar.ItemsSource = ItemsSource;
if (this.chartView.Axes.Count > 0
&& (!this.chartView.ActualAxes[0].IsNull() || !this.chartView.ActualAxes[0].DependentAxes.IsNull()))
foreach (var item in this.chartView.ActualAxes[0].DependentAxes)
{
this.chartView.ActualAxes[0].DependentAxes.Remove(item);
}
}
}
#endregion
#region Properties
private String[] chartTypesList;
public String[] ChartTypesList
{
get { return chartTypesList; }
set
{
chartTypesList = value;
OnPropertyChanged("ChartTypesList");
}
}
private String chartTypeSelectedValue;
public String ChartTypeSelectedValue
{
get { return chartTypeSelectedValue; }
set
{
chartTypeSelectedValue = value;
OnPropertyChanged("ChartTypeSelectedValue");
}
}
#endregion
#region INotifyPropertyChanged event and method
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (!PropertyChanged.IsNull())
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
#endregion
#region Preview Mouse events
private void chartView_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
var t = ((e.OriginalSource) as FrameworkElement).DataContext;
if (!t.IsNull() && e.OriginalSource.GetType().Equals(typeof(System.Windows.Shapes.Rectangle)))
{
Object a = null;
foreach (PropertyInfo item in ((e.OriginalSource) as FrameworkElement).DataContext.GetType().GetProperties())
{
a = item.GetValue(((e.OriginalSource) as FrameworkElement).DataContext, null);
break;
}
if (a == null)
a = "";
((FrameworkElement)(e.OriginalSource)).ToolTip = a;
}
}
#endregion
#region ReflectionReturn Seekers
public List<Control> FindAllControls(DependencyObject parent)
{
var list = new List<Control>() { };
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is Control)
{
list.Add(child as Control);
}
list.AddRange(FindAllControls(child));
}
return list;
}
public IEnumerable<FieldInfo> GetAllFields(Type t)
{
if (t.IsNull())
return Enumerable.Empty<FieldInfo>();
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
return t.GetFields(flags).Union(GetAllFields(t.BaseType));
}
#endregion
}
#region Enum
public enum ChartTypes
{
Bar,
Columns,
Pie,
Lines,
Area
}
#endregion
}

Answer 1
You can declare tooltips inside the ColumnDataPoint style. The simplest approach would be to add a new property to your model and bind to it.
For example, if the chart item class has 2 properties, add the 3rd property specially for tooltips:
public class ChartItem
{
public string Title { get; set; } // coil456
public double Value { get; set; } // 334
public string TooltipLabel
{
get { return string.Format("{0}({1})", this.Title, this.Value); } // coil456(334)
}
}
Then add a binding to the data point style:
<Style x:Key="ColumnDataPointStyle" TargetType="chart:ColumnDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type chart:ColumnDataPoint}">
<Rectangle ToolTipService.ToolTip="{Binding TooltipLabel}">
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The changed line: ToolTipService.ToolTip="{Binding TooltipLabel}". I used a simple binding because I bind to the DataContext, not to the template.
Then change your code-behind so that the column series uses the style:
case ChartTypes.Columns:
objChar = new ColumnSeries();
objChar.DataPointStyle = (Style)this.Resources["ColumnDataPointStyle"];
this.chartView.Series.Add(objChar);
break;
Similarly, you can add tooltips to axes by using the AxisLabelStyle property and a style for NumericAxisLabel.
Answer 3
Everything in the same way: add a property to the model and bind to it.
C# model
public class ChartItem
{
public string Title { get; set; }
public double Value { get; set; }
public string TooltipLabel
{
get { return string.Format("{0}({1})", this.Title, this.Value); }
}
public SolidColorBrush ColumnBrush
{
get
{
if (this.Value < 200)
{
return Brushes.Red;
}
else if (this.Value < 350)
{
return Brushes.Yellow;
}
else
{
return Brushes.Green;
}
}
}
}
Template
<Style x:Key="ColumnDataPointStyle" TargetType="chart:ColumnDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type chart:ColumnDataPoint}">
<Rectangle ToolTipService.ToolTip="{Binding TooltipLabel}" Fill="{Binding ColumnBrush}">
</Rectangle>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

Change WPF Tabcontrol Header Background based on ItemSource value

i try to change the background of my TabControl based on a bool which is declared in the ItemSource object. The ItemSource of the TabControl is an list of UC_Instance. I dont know where to start here respectively I dont know, how to tell the TabControl to look on each item which is binded separately.
If the getter (HasFailedConnections) in UC_Instance.cs returns true, it should color the background of the TabControl header red.
Hope someone can point me in the right direction.
TabControl.XAML
<TabControl x:Name="TabItems" VerticalAlignment="Top" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Padding="16,0,0,0" Style="{x:Null}" Height="270" ItemsSource="{Binding Path=AddionalInstanceList}" IsSynchronizedWithCurrentItem="True">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding DealerNumber, Mode=TwoWay}"/>
<Setter Property="MinWidth" Value="75"/>
<Setter Property="Foreground" Value="{StaticResource PrimaryHueMidForegroundBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" BorderThickness="1,1,1,1" BorderBrush="Gainsboro" CornerRadius="0,0,0,0" Margin="0,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="10,2"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PrimaryHueMidBrush}" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PrimaryHueLightBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
UC_Instance Class
using Devology.Lib.Internal;
using System;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
namespace Atbas.NET.UserControls
{
public partial class UC_Instance : UserControl
{
public bool HasFailedConnections
{
get
{
if(ServerAvailable == Enums.ConnectionTestResult.Failed || UpdaterAvailable == Enums.ConnectionTestResult.Failed || PortAvailable == Enums.ConnectionTestResult.Failed || DatabaseAvailable == Enums.ConnectionTestResult.Failed ||
ServerAvailable == Enums.ConnectionTestResult.None || UpdaterAvailable == Enums.ConnectionTestResult.None || PortAvailable == Enums.ConnectionTestResult.None || DatabaseAvailable == Enums.ConnectionTestResult.None)
{
return true;
}
else
{
return false;
}
}
}
public static DependencyProperty DatabaseCouldNotBeFoundProperty = DependencyProperty.Register("DatabaseCouldNotBeFound", typeof(string), typeof(UC_Instance));
public string DatabaseCouldNotBeFound
{
get => (string)GetValue(DatabaseCouldNotBeFoundProperty);
set => SetValue(DatabaseCouldNotBeFoundProperty, value);
}
public static DependencyProperty DatabaseNameProperty = DependencyProperty.Register("DatabaseName", typeof(string), typeof(UC_Instance));
public string DatabaseName
{
get => (string)GetValue(DatabaseNameProperty);
set => SetValue(DatabaseNameProperty, value);
}
public static DependencyProperty ServerProperty = DependencyProperty.Register("Server", typeof(string), typeof(UC_Instance));
public string Server
{
get => (string)GetValue(ServerProperty);
set => SetValue(ServerProperty, value);
}
public static DependencyProperty DatabasePortProperty = DependencyProperty.Register("DatabasePort", typeof(int), typeof(UC_Instance));
public int DatabasePort
{
get => (int)GetValue(DatabasePortProperty);
set => SetValue(DatabasePortProperty, value);
}
public static DependencyProperty DealerNumberProperty = DependencyProperty.Register("DealerNumber", typeof(string), typeof(UC_Instance));
public string DealerNumber
{
get => (string)GetValue(DealerNumberProperty);
set => SetValue(DealerNumberProperty, value);
}
public static DependencyProperty UpdaterIdProperty = DependencyProperty.Register("UpdaterId", typeof(int), typeof(UC_Instance));
public int UpdaterId
{
get => (int)GetValue(UpdaterIdProperty);
set => SetValue(UpdaterIdProperty, value);
}
public static DependencyProperty DataFromNetFolderProperty = DependencyProperty.Register("DataFromNetFolder", typeof(bool), typeof(UC_Instance));
public bool DataFromNetFolder
{
get => (bool)GetValue(DataFromNetFolderProperty);
set => SetValue(DataFromNetFolderProperty, value);
}
public static DependencyProperty ServerTestProperty = DependencyProperty.Register("ServerAvailable", typeof(Enums.ConnectionTestResult), typeof(UC_Instance));
public Enums.ConnectionTestResult ServerAvailable
{
get => (Enums.ConnectionTestResult)GetValue(ServerTestProperty);
set => SetValue(ServerTestProperty, value);
}
public static DependencyProperty PortTestProperty = DependencyProperty.Register("PortAvailable", typeof(Enums.ConnectionTestResult), typeof(UC_Instance));
public Enums.ConnectionTestResult PortAvailable
{
get => (Enums.ConnectionTestResult)GetValue(PortTestProperty);
set => SetValue(PortTestProperty, value);
}
public static DependencyProperty DatabaseTestProperty = DependencyProperty.Register("DatabaseAvailable", typeof(Enums.ConnectionTestResult), typeof(UC_Instance));
public Enums.ConnectionTestResult DatabaseAvailable
{
get => (Enums.ConnectionTestResult)GetValue(DatabaseTestProperty);
set => SetValue(DatabaseTestProperty, value);
}
public static DependencyProperty UpdaterTestProperty = DependencyProperty.Register("UpdaterAvailable", typeof(Enums.ConnectionTestResult), typeof(UC_Instance));
public Enums.ConnectionTestResult UpdaterAvailable
{
get => (Enums.ConnectionTestResult)GetValue(UpdaterTestProperty);
set => SetValue(UpdaterTestProperty, value);
}
private void TextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = new Regex("[^0-9]+").IsMatch(e.Text);
}
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
String text = (String)e.DataObject.GetData(typeof(String));
if (new Regex("[^0-9]+").IsMatch(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
public UC_Instance()
{
InitializeComponent();
DatabaseCouldNotBeFound = "Die Datenbank wird beim Test automatisch anhand der hinterlegten Händler-Nr. ermittelt.";
}
}
}

Set Border.Brush property according to Mouse State and Position

I'm trying to change my UserControl's Border.BorderBrush property depending on if the Mouse enters, leaves, or is down on my UserControl. I've tried doing it explicitly in the code behind, but anytime the Border.BorderBrush property is changed, the border disappears.
I went down a string of possible solutions with no avail. As of my current codebase I'm attempting to use a Style and Triggers to manage it for me. My issue there is that there is no property for IsMouseDown unless you're dealing with a Button (at least that's what I've gathered from reading), so I defined a property for that.
Just when I think it's going to work, the border can't find my Style defined in UserControl.Resources.
I've exhausted everything I know to do, any help would be extremely appreciated.
XAML:
<UserControl x:Class="OSK.Resources.Themes.Default.Key"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OSK.Resources.Themes.Default"
mc:Ignorable="d"
x:Name="OSKuwuDefaultKey"
d:DesignHeight="48" d:DesignWidth="48">
<UserControl.Style>
<Style x:Name="KeyStyle">
<Style.Triggers>
<Trigger Property="Border.IsMouseOver" Value="true">
<Setter Property="Border.BorderBrush" Value="{Binding Path=BrushHover, RelativeSource={RelativeSource Self}}" />
</Trigger>
<Trigger Property="Border.IsMouseOver" Value="false">
<Setter Property="Border.BorderBrush" Value="{Binding Path=BrushNormal, RelativeSource={RelativeSource Self}}" />
</Trigger>
<Trigger Property="local:Key.IsMouseDown" Value="true">
<Setter Property="Border.BorderBrush" Value="{Binding Path=BrushDown, RelativeSource={RelativeSource Self}}" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Style>
<Border x:Name="key" Background="Transparent" Width="{Binding Path=Width, ElementName=OSKuwuDefaultKey}" Height="{Binding Path=Height, ElementName=OSKuwuDefaultKey}" BorderBrush="{Binding BorderBrush, ElementName=OSKuwuDefaultKey}" CornerRadius="{Binding Path=CornerRadius, ElementName=OSKuwuDefaultKey}" BorderThickness="{Binding Path=OutlineThickness, ElementName=OSKuwuDefaultKey}" MouseEnter="Key_MouseEnter" MouseLeave="Key_MouseLeave" MouseDown="Key_MouseDown" MouseUp="Key_MouseUp">
<Canvas>
<Label Content="{Binding SuperText, ElementName=OSKuwuDefaultKey}" Canvas.Top="6" Canvas.Left="8" />
<Label Content="{Binding SubText, ElementName=OSKuwuDefaultKey}" Canvas.Top="20" Canvas.Right="4" />
</Canvas>
</Border>
Code-Behind:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace OSK.Resources.Themes.Default
{
/// <summary>
/// Interaction logic for Key.xaml
/// </summary>
public partial class Key : UserControl
{
public static readonly DependencyProperty SuperTextProperty = DependencyProperty.Register("SuperText", typeof(string), typeof(Key), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender));
public string SuperText
{
get
{
return (string)GetValue(SuperTextProperty);
}
set
{
SetValue(SuperTextProperty, value);
}
}
public static readonly DependencyProperty SubTextProperty = DependencyProperty.Register("SubText", typeof(string), typeof(Key), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender));
public string SubText
{
get
{
return (string)GetValue(SubTextProperty);
}
set
{
SetValue(SubTextProperty, value);
}
}
public static readonly DependencyProperty BrushNormalProperty = DependencyProperty.Register("BrushNormal", typeof(Brush), typeof(Key), new FrameworkPropertyMetadata(Brushes.LightSlateGray));
public Brush BrushNormal
{
get
{
return (Brush)GetValue(BrushNormalProperty);
}
set
{
SetValue(BrushNormalProperty, value);
}
}
public static readonly DependencyProperty BrushHoverProperty = DependencyProperty.Register("BrushHover", typeof(Brush), typeof(Key), new FrameworkPropertyMetadata(Brushes.LightSteelBlue));
public Brush BrushHover
{
get
{
return (Brush)GetValue(BrushHoverProperty);
}
set
{
SetValue(BrushHoverProperty, value);
}
}
public static readonly DependencyProperty BrushDownProperty = DependencyProperty.Register("BrushDown", typeof(Brush), typeof(Key), new FrameworkPropertyMetadata(Brushes.SlateGray));
public Brush BrushDown
{
get
{
return (Brush)GetValue(BrushDownProperty);
}
set
{
SetValue(BrushDownProperty, value);
}
}
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(int), typeof(Key), new FrameworkPropertyMetadata(4, FrameworkPropertyMetadataOptions.AffectsRender));
public int CornerRadius
{
get
{
return (int)GetValue(CornerRadiusProperty);
}
set
{
SetValue(CornerRadiusProperty, value);
}
}
public static readonly DependencyProperty OutlineThicknessProperty = DependencyProperty.Register("OutlineThickness", typeof(Thickness), typeof(Key), new FrameworkPropertyMetadata(new Thickness(1), FrameworkPropertyMetadataOptions.AffectsRender));
public Thickness OutlineThickness
{
get
{
return (Thickness)GetValue(OutlineThicknessProperty);
}
set
{
SetValue(OutlineThicknessProperty, value);
}
}
public static DependencyProperty IsMouseDownProperty = DependencyProperty.RegisterAttached("IsMouseDown", typeof(bool), typeof(Key), new FrameworkPropertyMetadata(default(bool)));
public bool IsMouseDown
{
get
{
return (bool)GetValue(IsMouseDownProperty);
}
set
{
SetValue(IsMouseDownProperty, value);
}
}
public Key()
{
InitializeComponent();
}
private void Key_MouseEnter(object sender, MouseEventArgs e)
{
//this.SetValue(Key.BorderBrushProperty, BrushNormal);
//Console.WriteLine(GetValue(Key.BorderBrushProperty));
}
private void Key_MouseLeave(object sender, MouseEventArgs e)
{
//this.SetValue(Key.BorderBrushProperty, BrushHover);
}
private void Key_MouseDown(object sender, MouseButtonEventArgs e)
{
//this.SetValue(Key.BorderBrushProperty, BrushDown);
this.SetValue(Key.IsMouseDownProperty, true);
}
private void Key_MouseUp(object sender, MouseButtonEventArgs e)
{
this.SetValue(Key.IsMouseDownProperty, false);
}
}
}
I got it working. The original post has the final working code.
And the layout should look like this, without explicitly setting the size of the Border:
<Border x:Name="key" Background="Transparent"
BorderBrush="{Binding BorderBrush, ElementName=OSKuwuDefaultKey}"
CornerRadius="{Binding CornerRadius, ElementName=OSKuwuDefaultKey}"
BorderThickness="{Binding OutlineThickness, ElementName=OSKuwuDefaultKey}"
MouseEnter="Key_MouseEnter"
MouseLeave="Key_MouseLeave"
MouseDown="Key_MouseDown"
MouseUp="Key_MouseUp">
<Canvas>
...
</Canvas>
</Border>

Dynamic Axis in WPF Toolkit LineSeries Chart

I am using WPF Toolkit for charting. I am using a LineSeries for displaying the data change per second. Currently, I am able to update the graph as new points are added. But the X-Axis scale is fixed from 0 to 60 automatically. What I want is, after the first cycle, instead of the data plot showing from the starting of the axis, I want the X-Axis to shift by one division, like it is in an ECG display.
I managed to find the answer on my own. Please give suggestions fro improving the answer.
namespace WpfChartExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<ChartData> chartData;
ChartData objChartData;
Thread MyThread;
public MainWindow()
{
InitializeComponent();
chartData = new ObservableCollection<ChartData>();
DateTime dtnow = DateTime.Now;
objChartData = new ChartData() { Name = dtnow, Value = 0.0 };
chartData.Add(objChartData);
chartData.Add(new ChartData() { Name = (dtnow + TimeSpan.FromSeconds(2)), Value = new Random().NextDouble() * 100 });
chartData.Add(new ChartData() { Name = (dtnow + TimeSpan.FromSeconds(4)), Value = new Random().NextDouble() * 100 });
xAxis.Minimum = chartData[0].Name;
simChart.DataContext = chartData;
MyThread = new Thread(new ThreadStart(StartChartDataSimulation));
}
public void StartChartDataSimulation()
{
int i = 0;
while (true)
{
Dispatcher.Invoke(new Action(() =>
{
var data = new ChartData() { Name = DateTime.Now, Value = new Random().NextDouble() * 100 };
chartData.Add(data);
if (chartData.Count % 40 == 0 && i == 0)
{
xAxis.Minimum = chartData[i + 1].Name;
i++;
}
if (i >= 1)
{
xAxis.Minimum = chartData[i + 1].Name;
i++;
}
}));
Thread.Sleep(1000);
}
}
private void btnStartStop_Click(object sender, RoutedEventArgs e)
{
if ((string)btnStartStop.Content == "Start Simulation")
{
if (MyThread.ThreadState == ThreadState.Unstarted)
{
MyThread.Start();
}
else if (MyThread.ThreadState == ThreadState.Suspended)
{
MyThread.Resume();
}
btnStartStop.Content = "Stop Simulation";
}
else
{
MyThread.Suspend();
btnStartStop.Content = "Start Simulation";
}
}
private void Window_Closing(object sender, CancelEventArgs e)
{
if (MyThread.ThreadState == ThreadState.Running ||MyThread.ThreadState == ThreadState.WaitSleepJoin)
{
MyThread.Suspend();
}
Application.Current.Shutdown();
}
}
public class ChartData : INotifyPropertyChanged
{
DateTime _Name;
double _Value;
#region properties
public DateTime Name
{
get
{
return _Name;
}
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
public double Value
{
get
{
return _Value;
}
set
{
_Value = value;
OnPropertyChanged("Value");
}
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
<Window x:Class="WpfChartExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:chrt="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
Title="MainWindow" Height="350" Width="525" Closing="Window_Closing">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<chrt:Chart x:Name="simChart" Title="Simulation" Background="Beige">
<chrt:LineSeries IndependentValueBinding="{Binding Name}"
DependentValueBinding="{Binding Value}"
ItemsSource="{Binding}"
Background="DarkGray">
<chrt:LineSeries.DataPointStyle>
<Style TargetType="{x:Type chrt:LineDataPoint}">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="Width" Value="5" />
<Setter Property="Height" Value="5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chrt:LineDataPoint">
<Grid x:Name="Root" Opacity="1">
<ToolTipService.ToolTip>
<StackPanel Margin="2,2,2,2">
<ContentControl Content="{TemplateBinding IndependentValue}"
ContentStringFormat="X-Value: {0:HH:mm:ss}"/>
<ContentControl Content="{TemplateBinding DependentValue}"
ContentStringFormat="Y-Value: {0:###.###}"/>
</StackPanel>
</ToolTipService.ToolTip>
<Ellipse StrokeThickness="{TemplateBinding BorderThickness}"
Stroke="{TemplateBinding BorderBrush}"
Fill="{TemplateBinding Background}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</chrt:LineSeries.DataPointStyle>
<chrt:LineSeries.IndependentAxis>
<chrt:DateTimeAxis Name="xAxis" ShowGridLines="True" Orientation="X">
<chrt:DateTimeAxis.AxisLabelStyle>
<Style TargetType="chrt:DateTimeAxisLabel">
<Setter Property="StringFormat" Value="{}{0:mm:ss}" />
</Style>
</chrt:DateTimeAxis.AxisLabelStyle>
</chrt:DateTimeAxis>
</chrt:LineSeries.IndependentAxis>
<chrt:LineSeries.DependentRangeAxis>
<chrt:LinearAxis Orientation="Y" ShowGridLines="True" Minimum="-50" Maximum="50"></chrt:LinearAxis>
</chrt:LineSeries.DependentRangeAxis>
<chrt:LineSeries.Title>
<TextBlock TextAlignment="Center">Time<LineBreak/>vs.<LineBreak/>Random<LineBreak/>Data</TextBlock>
</chrt:LineSeries.Title>
</chrt:LineSeries>
</chrt:Chart>
<Button Name="btnStartStop" Width="Auto" Height="30" Grid.Row="1" HorizontalAlignment="Right" Margin="10" Click="btnStartStop_Click">Start Simulation</Button>
</Grid>

WPF Binding Border Color in Custom Button

I have an application where a number of custom buttons are dynamically generated within a WrapPanel. All works fine and I am able to assign border thickness, ImageSource, Content etc. as I generate the buttons in code. The customer now has a requirement to allow them to choose border colours for individual buttons and try as I might I cannot figure out the correct binding scenario. I'm on a steep WPF learning curve here so it may be that my initial design is somewhat off kilter.
In my Generic.XAML I have the button specified thus:
<Style TargetType="{x:Type local:LauncherButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:LauncherButton}">
<Border Name="LauncherButtonBorder" BorderThickness="{TemplateBinding BThickness}"
CornerRadius="10" Background="White" >
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="SteelBlue" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="PaleGoldenrod" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<DockPanel LastChildFill="True" Background="White" Margin="3">
<TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Center"
Foreground="{DynamicResource TaskButtonTextBrush}" FontWeight="Bold"
Margin="5,0,0,0" VerticalAlignment="Center" FontSize="10"
Background="Transparent" DockPanel.Dock="Bottom" TextWrapping="Wrap" />
<Image Source="{TemplateBinding ImageSource}" Stretch="Uniform" />
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want to dynamically change in c# the border colours that are currently set to static SteelBlue and PaleGoldenrod.
The button class is defined thus:
public class LauncherButton : ButtonBase
{
static LauncherButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LauncherButton), new FrameworkPropertyMetadata(typeof(LauncherButton)));
}
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public Thickness BThickness
{
get { return (Thickness) GetValue(BThicknessProperty); }
set { SetValue(BThicknessProperty,value);}
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(LauncherButton), new UIPropertyMetadata(null));
public static readonly DependencyProperty BThicknessProperty =
DependencyProperty.Register("BThickness", typeof(Thickness), typeof(LauncherButton), new UIPropertyMetadata(null));
}
and I'm binding some of the properties to an instance of the following class:
public class CustomButton:INotifyPropertyChanged
{
private string _type;
private string _buttonId;
private string _name;
private string _image;
private string _link;
private string _parent;
private List<CustomButton> _children;
private bool _isExpanded;
private bool _isSelected;
public string ButtonId
{
get { return _buttonId; }
set
{
if (value == _buttonId) return;
_buttonId = value;
OnPropertyChanged("ButtonId");
}
}
public string Type
{
get { return _type; }
set
{
if (value == _type) return;
_type = value;
OnPropertyChanged("Type");
}
}
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged("Name");
}
}
public string Image
{
get { return _image; }
set
{
if (value == _image) return;
_image = value;
OnPropertyChanged("Image");
}
}
public string Link
{
get { return _link; }
set
{
if (value == _link) return;
_link = value;
OnPropertyChanged("Link");
}
}
public string Parent
{
get { return _parent; }
set
{
if (value == _parent) return;
_parent = value;
OnPropertyChanged("Parent");
}
}
public List<CustomButton> Children
{
get { return _children; }
set
{
if (Equals(value, _children)) return;
_children = value;
OnPropertyChanged("Children");
}
}
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (value.Equals(_isExpanded)) return;
_isExpanded = value;
OnPropertyChanged("IsExpanded");
}
}
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value.Equals(_isSelected)) return;
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Are you trying to make the two brushes used for Border.BorderBrush dynamic?
If so you can address it in a few ways.
Add two dependency properties to LauncherButton for say NormalBorderBrush and MouseOverBorderBrush and then set it as you wish when you use the Button. Now to get the Border to use this, within it's Style where you set SteelBlue or PaleGoldenRod, apply a RelativeSource FindAncestor binding with AncestorType as local:LauncherButton and point it to the corresponding Brush(NormalBorderBrush or MouseOverBorderBrush)
Example:
public class LauncherButton : ButtonBase {
...
public static readonly DependencyProperty NormalBorderBrushProperty =
DependencyProperty.Register("NormalBorderBrush", typeof(Brush), typeof(LauncherButton),
new UIPropertyMetadata(Brushes.Blue));
public static readonly DependencyProperty MouseOverBorderBrushProperty =
DependencyProperty.Register("MouseOverBorderBrush", typeof(Brush), typeof(LauncherButton),
new UIPropertyMetadata(Brushes.Red));
public Brush NormalBorderBrush
{
get { return (Brush)GetValue(NormalBorderBrushProperty); }
set { SetValue(NormalBorderBrushProperty, value); }
}
public Brush MouseOverBorderBrush
{
get { return (Brush)GetValue(MouseOverBorderBrushProperty); }
set { SetValue(MouseOverBorderBrushProperty, value); }
}
}
in xaml:
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderBrush"
Value="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:LauncherButton}},
Path=NormalBorderBrush}" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="BorderBrush"
Value="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:LauncherButton}},
Path=MouseOverBorderBrush}" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
and usage:
<local:LauncherButton BThickness="5"
Content="Hellooooo"
MouseOverBorderBrush="Green"
NormalBorderBrush="Aqua" />
Sample Download - This does not contain the converter for Brush usage, that should be easy enough to implement.
OR You could have two brushes defined as dynamic resources and override their color's from your code when you need to.
OR You can use the Button's BorderBrush property which it already has and apply this to the Border with a TemplateBinding BorderBrush. Now this would mean you need to switch the BorderBrush accordingly when your IsMouseOver state change occurs.
OR you could even go to extents of retrieving the button's Style and getting a reference to the Border element by finding it via it's Name and then tweaking it at run-time.
Personally I'd opt for option 1. Finally use a converter or likewise in the Binding to make it MVVM friendly.

How do you animate a line on a canvas in C#?

How would you make a line slowly draw across the screen?
I am trying to animate a line on a canvas in a C#/WPF project.
I would like to use C# code and not XAML.
I Have a running sample that uses the MVVM Pattern and creates Lines within a ListBox that has a Canvas as its ItemsPanel.
I actually made it for this question, but the OP kind of dissapeared and never contacted me about it.
This is what it looks like in my computer:
The main part of it is this:
<ListBox ItemsSource="{Binding}" x:Name="lst" Height="500" Width="500">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="FocusVisualStyle">
<Setter.Value>
<Style TargetType="Control">
<Setter Property="Opacity" Value="0"/>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Line X1="{Binding X1}" Y1="{Binding Y1}"
X2="{Binding X2}" Y2="{Binding Y2}"
StrokeThickness="{Binding Thickness}"
Opacity="{Binding Opacity}"
x:Name="Line">
<Line.Stroke>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="{Binding Color1}" Offset="0"/>
<GradientStop Color="{Binding Color2}" Offset="1"/>
</LinearGradientBrush>
</Line.Stroke>
</Line>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect" TargetName="Line">
<Setter.Value>
<DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
ViewModel:
public class LineViewModel : INotifyPropertyChanged
{
#region Timer-based Animation
private System.Threading.Timer Timer;
private static Random Rnd = new Random();
private bool _animate;
public bool Animate
{
get { return _animate; }
set
{
_animate = value;
NotifyPropertyChanged("Animate");
if (value)
StartTimer();
else
StopTimer();
}
}
private int _animationSpeed = 1;
public int AnimationSpeed
{
get { return _animationSpeed; }
set
{
_animationSpeed = value;
NotifyPropertyChanged("AnimationSpeed");
if (Timer != null)
Timer.Change(0, 100/value);
}
}
private static readonly List<int> _animationSpeeds = new List<int>{1,2,3,4,5};
public List<int> AnimationSpeeds
{
get { return _animationSpeeds; }
}
public void StartTimer()
{
StopTimer();
Timer = new Timer(x => Timer_Tick(), null, 0, 100/AnimationSpeed);
}
public void StopTimer()
{
if (Timer != null)
{
Timer.Dispose();
Timer = null;
}
}
private void Timer_Tick()
{
X1 = X1 + Rnd.Next(-2, 3);
Y1 = Y1 + Rnd.Next(-2, 3);
X2 = X2 + Rnd.Next(-2, 3);
Y2 = Y2 + Rnd.Next(-2, 3);
}
#endregion
#region Coordinates
private double _x1;
public double X1
{
get { return _x1; }
set
{
_x1 = value;
NotifyPropertyChanged("X1");
}
}
private double _y1;
public double Y1
{
get { return _y1; }
set
{
_y1 = value;
NotifyPropertyChanged("Y1");
}
}
private double _x2;
public double X2
{
get { return _x2; }
set
{
_x2 = value;
NotifyPropertyChanged("X2");
}
}
private double _y2;
public double Y2
{
get { return _y2; }
set
{
_y2 = value;
NotifyPropertyChanged("Y2");
}
}
#endregion
#region Other Properties
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
private double _thickness;
public double Thickness
{
get { return _thickness; }
set
{
_thickness = value;
NotifyPropertyChanged("Thickness");
}
}
public Color Color1 { get; set; }
public Color Color2 { get; set; }
private double _opacity = 1;
public double Opacity
{
get { return _opacity; }
set
{
_opacity = value;
NotifyPropertyChanged("Opacity");
}
}
#endregion
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
Application.Current.Dispatcher.BeginInvoke((Action)(() =>
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}));
}
#endregion
}
Edit: Source code now on GitHub
You will need to use a Storyboard and animate the Line.X2 and Line.Y2 Properties. See if this works for you.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Canvas Name="myCanvas">
<Button Canvas.Left="248" Canvas.Top="222" Content="Button" Height="23" Name="button1" Width="75" Click="button1_Click" />
</Canvas>
</Window>
Button Click Event
private void button1_Click(object sender, RoutedEventArgs e)
{
Line line = new Line();
myCanvas.Children.Add(line);
line.Stroke = Brushes.Red;
line.StrokeThickness = 2;
line.X1 = 0;
line.Y1 = 0;
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation(line.Y2 , 100, new Duration(new TimeSpan(0, 0, 1)));
DoubleAnimation da1 = new DoubleAnimation(line.X2, 100, new Duration(new TimeSpan(0, 0, 1)));
Storyboard.SetTargetProperty(da, new PropertyPath("(Line.Y2)"));
Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X2)"));
sb.Children.Add(da);
sb.Children.Add(da1);
line.BeginStoryboard(sb);
}

Categories