How do you animate a line on a canvas in C#? - 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);
}

Related

How to properly set up a TwoWay Binding in wpf with Code

I have been trying to set up a two-way binding in wpf. There is a canvas that is populated with ContentControls, each one containing a filled rectangle. Through a thumb, each ContentControl can be made larger and therefore have a changed width.
These ContentControls have been generated by code and live within a class (CanvasElement), which is used for other calculations.
I'd like to set up a two way binding between the ContentControl Property Width and the public double variable Width within the CanvasElement class. When the Thumb is used to change the width of the contentControl, the Width of the CanvasElement is updated, but the other way it doesn't work.
Here is what I have so far:
public class CanvasElement
{
private double width;
public double height;
private Point location; // This is the upper left point of the rectangle
public Brush color;
public string UID;
public ContentControl canvasElement;
public CanvasElement(Point location, double width, double height, Brush color, string UID)
{
this.location = location;
this.width = width;
this.height = height;
this.color = color;
this.UID = UID;
canvasElement = new ContentControl() { Width = this.width, Height = this.height, Uid = UID };
Canvas.SetLeft(canvasElement, this.location.X);
Canvas.SetTop(canvasElement, this.location.Y);
canvasElement.Content = new Rectangle() {
IsHitTestVisible = false,
Fill = this.color,
Stroke =Brushes.LightGray,
StrokeThickness = 2,
Margin = new Thickness(0,5,0,5),
RadiusX = 10,
RadiusY = 10};
addBinding();
}
private void addBinding()
{
Binding widthBinding = new Binding();
widthBinding.Source = this;
widthBinding.Path = new PropertyPath("Width");
widthBinding.Mode = BindingMode.TwoWay;
widthBinding.NotifyOnSourceUpdated = true;
widthBinding.NotifyOnTargetUpdated = true;
//widthBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(canvasElement, ContentControl.WidthProperty, widthBinding);
}
public double Width
{
get
{
return width;
}
set
{
if(width != value)
{
width = value;
OnPropertyChanged();
}
}
}
As well as:
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I am thankful for every hint I can get!
Thanks for helping out!
As Clemens pointed out in his comment the ItemsControl is the right way to do this. As I have different UIElements that are added to the canvas I needed to add an ItemsControl.ItemTemplateSelector as well as an ItemsControlItemContainerStyleSelector.
XAML
<AdornerDecorator ClipToBounds="True">
<ItemsControl ItemsSource="{Binding CanvasElementList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="FloralWhite"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyleSelector>
<local:CustomStyleSelector>
<local:CustomStyleSelector.CanvasStyle_TL>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=X, Mode=TwoWay}"/>
<Setter Property="Canvas.Top" Value="{Binding Path=Y, Mode=TwoWay}"/>
</Style>
</local:CustomStyleSelector.CanvasStyle_TL>
<local:CustomStyleSelector.CanvasStyle_TR>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Right" Value="{Binding Path=X, Mode=TwoWay}"/>
<Setter Property="Canvas.Top" Value="{Binding Path=Y, Mode=TwoWay}"/>
</Style>
</local:CustomStyleSelector.CanvasStyle_TR>
<local:CustomStyleSelector.CanvasStyle_BL>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=X, Mode=TwoWay}"/>
<Setter Property="Canvas.Bottom" Value="{Binding Path=Y, Mode=TwoWay}"/>
</Style>
</local:CustomStyleSelector.CanvasStyle_BL>
<local:CustomStyleSelector.CanvasStyle_BR>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Right" Value="{Binding Path=X, Mode=TwoWay}"/>
<Setter Property="Canvas.Bottom" Value="{Binding Path=Y, Mode=TwoWay}"/>
</Style>
</local:CustomStyleSelector.CanvasStyle_BR>
<local:CustomStyleSelector.LineStyle>
<Style TargetType="ContentPresenter">
</Style>
</local:CustomStyleSelector.LineStyle>
</local:CustomStyleSelector>
</ItemsControl.ItemContainerStyleSelector>
<ItemsControl.ItemTemplateSelector>
<local:CustomTemplateSelectors>
<local:CustomTemplateSelectors.LabelTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Text}"
FontWeight="{Binding FontWeight}"
FontSize="{Binding FontSize}"/>
</DataTemplate>
</local:CustomTemplateSelectors.LabelTemplate>
<local:CustomTemplateSelectors.LineTemplate>
<DataTemplate>
<Line X1="{Binding X1}"
X2="{Binding X2}"
Y1="{Binding Y1}"
Y2="{Binding Y2}"
Stroke="{Binding Stroke}"
StrokeThickness="{Binding StrokeThickness}"
StrokeDashArray="{Binding StrokeDashArray}"/>
</DataTemplate>
</local:CustomTemplateSelectors.LineTemplate>
<local:CustomTemplateSelectors.CanvasElementTemplate>
<DataTemplate>
<ContentControl Width="{Binding Path=Width, Mode=TwoWay}" Height="{Binding Path=Height, Mode=TwoWay}"
Style="{StaticResource ResourceKey=DesignerItemStyle}"
MouseDoubleClick="ContentControl_MouseDoubleClick">
<Rectangle Fill="{Binding Color}"
Stroke="LightGray"
StrokeThickness="2"
Margin="0,5,0,5"
RadiusX="10"
RadiusY="10"
IsHitTestVisible="False"/>
</ContentControl>
</DataTemplate>
</local:CustomTemplateSelectors.CanvasElementTemplate>
</local:CustomTemplateSelectors>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
</AdornerDecorator>
Code
In the corresponding .cs file there are these ObservableCollections and the CompositeCollection. The later is the element that binds to the ItemsControl. To add new elements you have to add elements to the Observable Collections
CanvasElementList4Canvas = new ObservableCollection<CanvasElement>();
LineList4Canvas = new ObservableCollection<CustomLine>();
LabelList4Canvas = new ObservableCollection<LabelTextBlock>();
CanvasElementList = new CompositeCollection();
CanvasElementList.Add(new CollectionContainer() { Collection = CanvasElementList4Canvas });
CanvasElementList.Add(new CollectionContainer() { Collection = LineList4Canvas });
CanvasElementList.Add(new CollectionContainer() { Collection = LabelList4Canvas });
To set up the binding the CustomLine Class is shown here. The CanvasElement and the LabelTextBlock class are set up in the same way.
CustomLine
public class CustomLine : INotifyPropertyChanged
{
private double _X1;
private double _X2;
private double _Y1;
private double _Y2;
private int _strokeThickness = 3;
private Brush _stroke = Brushes.Black;
private DoubleCollection _strokeDashArray = new DoubleCollection() { 1.0, 0.0 };
public double X1 { get { return _X1; } set { if (_X1 != value) { _X1 = value; NotifyPropertyChanged("X1"); } } }
public double X2 { get { return _X2; } set { if (_X2 != value) { _X2 = value; NotifyPropertyChanged("X2"); } } }
public double Y1 { get { return _Y1; } set { if (_Y1 != value) { _Y1 = value; NotifyPropertyChanged("Y1"); } } }
public double Y2 { get { return _Y2; } set { if (_Y2 != value) { _Y2 = value; NotifyPropertyChanged("Y2"); } } }
public int StrokeThickness { get { return _strokeThickness; } set { if (_strokeThickness != value) { _strokeThickness = value; NotifyPropertyChanged("StrokeThickness"); } } }
public Brush Stroke { get { return _stroke; } set { if (_stroke != value) { _stroke = value; NotifyPropertyChanged("Stroke"); } } }
public DoubleCollection StrokeDashArray { get { return _strokeDashArray; } set { if (_strokeDashArray != value) { _strokeDashArray = value; NotifyPropertyChanged("StrokeDashArray"); } } }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and finally the custom selectors are needed to use the right template and the right style for within the canvas:
public class CustomTemplateSelectors : DataTemplateSelector
{
public DataTemplate CanvasElementTemplate { get; set; }
public DataTemplate LineTemplate { get; set; }
public DataTemplate LabelTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is CanvasElement)
return CanvasElementTemplate;
else if (item is CustomLine)
return LineTemplate;
else if (item is LabelTextBlock)
return LabelTemplate;
else return base.SelectTemplate(item, container);
}
}
public class CustomStyleSelector : StyleSelector
{
public Style CanvasStyle_TL { get; set; }
public Style CanvasStyle_TR { get; set; }
public Style CanvasStyle_BL { get; set; }
public Style CanvasStyle_BR { get; set; }
public Style LineStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is CanvasElement)
return CanvasStyle_TL;
else if (item is CustomLine)
return LineStyle;
else if (item is LabelTextBlock)
{
var tempItem = item as LabelTextBlock;
if (tempItem.Tag == "TL")
return CanvasStyle_TL;
else if (tempItem.Tag == "TR")
return CanvasStyle_TR;
else if (tempItem.Tag == "BL")
return CanvasStyle_BL;
else if (tempItem.Tag == "BR")
return CanvasStyle_BR;
else return base.SelectStyle(item, container);
}
else return base.SelectStyle(item, container);
}
}

Getting binding of transform property from code behind

I have code below inside Grid:
<Grid.RenderTransform>
<TranslateTransform
X="{Binding X, Converter={StaticResource HorizontalPositionConverter}}"
Y="{Binding Y, Converter={StaticResource VerticalPositionConverter}}"
/>
</Grid.RenderTransform>
How can I get binding of TranslateTransform.X or TranslateTransform.Y in code behind? I found this question but solution works for non-nested dependency properties. What to do when they are? I cannot consider binding to entire RenderTransform. I am developing winrt app, so multibinding is out of the game.
Here is the code behind binding. I didn't use a converter because my X and Y are defined double. In order to make a correct binding you have to use dependecy property or another notification mechanism (like INotifyPropertyChanged implementation). Here is code behind binding solution (not MVVM). I've added the button to test the moving.
1. XAML code:
<Window x:Class="TransformBindingSoHelpAttempt.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" x:Name="This">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RenderTransform>
<TranslateTransform
X="{Binding ElementName=This, Path=X, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Y="{Binding ElementName=This, Path=Y, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</Grid.RenderTransform>
<Button Content="Click" Width="100" Height="100" Click="ButtonBase_OnClick"></Button>
</Grid>
2. Code behind :
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty XProperty = DependencyProperty.Register(
"X", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
public double X
{
get { return (double) GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty YProperty = DependencyProperty.Register(
"Y", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
private static double _position;
public double Y
{
get { return (double) GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
X = ++_position;
Y = _position;
}
}
Update 1:
Here is code-behind based solution, there is no binding in XAML:
3. Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty XProperty = DependencyProperty.Register(
"X", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
public double X
{
get { return (double) GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty YProperty = DependencyProperty.Register(
"Y", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
private static double _position;
public double Y
{
get { return (double) GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
X = ++_position;
Y = _position;
}
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var grid = sender as Grid;
if(grid == null) return;
var transform = grid.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = InitTransformBinding();
grid.RenderTransform = transform;
}
else
{
InitTransformBinding(transform);
}
}
private TranslateTransform InitTransformBinding(TranslateTransform t = null)
{
var transform = t ?? new TranslateTransform();
var xBinding = new Binding();
xBinding.Source = this;
xBinding.Path = new PropertyPath("X");
xBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
xBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(transform, TranslateTransform.XProperty, xBinding);
var yBinding = new Binding();
yBinding.Source = this;
yBinding.Path = new PropertyPath("Y");
yBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
yBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(transform, TranslateTransform.YProperty, yBinding);
return transform;
}
}
4. XAML code:
<Window x:Class="TransformBindingSoHelpAttempt.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" x:Name="This">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Loaded="FrameworkElement_OnLoaded">
<Button Content="Click" Width="100" Height="100" Click="ButtonBase_OnClick"></Button>
</Grid>
Update 2, here on each button click you will scale the grid.
5. Xaml code:
Window x:Class="TransformBindingSoHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:transformBindingSoHelpAttempt="clr-namespace:TransformBindingSoHelpAttempt"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Window.DataContext>
<transformBindingSoHelpAttempt:MainViewModel/>
</Window.DataContext>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView ItemsSource="{Binding Items}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type transformBindingSoHelpAttempt:ItemDataContext}">
<Grid>
<Grid.RenderTransform>
<ScaleTransform
ScaleX="{Binding X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScaleY="{Binding Y, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid.RenderTransform>
<Button Content="{Binding ButtonContent}" Command="{Binding ButtonCommand}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
6. View models:
public class MainViewModel:BaseObservableObject
{
public MainViewModel()
{
Items = new ObservableCollection<ItemDataContext>(new List<ItemDataContext>
{
new ItemDataContext{ButtonContent = "A", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "B", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "C", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "D", X = 1.0, Y = 1.0},
});
}
public ObservableCollection<ItemDataContext> Items { get; set; }
}
public class ItemDataContext:BaseObservableObject
{
private ICommand _buttonCommand;
private object _buttonContent;
private double _x;
private double _y;
public double X
{
get { return _x; }
set
{
_x = value;
OnPropertyChanged();
}
}
public double Y
{
get { return _y; }
set
{
_y = value;
OnPropertyChanged();
}
}
public ICommand ButtonCommand
{
get { return _buttonCommand ?? (_buttonCommand = new DelegateCommand(Target)); }
}
public object ButtonContent
{
get { return _buttonContent; }
set
{
_buttonContent = value;
OnPropertyChanged();
}
}
private void Target(object obj)
{
X += 0.2;
Y += 0.2;
}
}
7. How it is looks like:
Please keep in mind that the last update solution is based on LayouTransform and re-build the view on each button click (makes it to be scaled).
Regards,

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>

Moving a grid to where I click, with animation

I'm trying to make a collection of tabs move to where ever I click on a canvas. I managed to get it working with fixed values, but cannot get it to work with a left mouse button click.
Here's my code:
public partial class MainWindow : Window
{
User Player = new User();
ThicknessConverter perimeter = new ThicknessConverter();
Inventory Inventory = new Inventory();
BreadCrumb Crumb = new BreadCrumb();
Locations Locations = new Locations();
PointAnimation myPointAnimation = new PointAnimation();
ThicknessAnimation myThicknessAnimation = new ThicknessAnimation();
DoubleAnimation da = new DoubleAnimation();
Point p = new Point();
private void Hansel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
da.From = Canvas.GetLeft(Hansel);
da.From = Canvas.GetTop(Hansel);
p.X = Mouse.GetPosition(PlayArea);
da.To = p.X; //Convert.ToDouble(PointToScreen(Mouse.GetPosition(this.)));
da.Duration = new Duration(TimeSpan.FromSeconds(2));
Hansel.BeginAnimation(Canvas.LeftProperty, da);
}
}
So, how would I go about getting the mouse position and converting those points to double coordinates?
<Window x:Class="MiscSamples.ClickToMove"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ClickToMove" Height="300" Width="300">
<ItemsControl ItemsSource="{Binding}" PreviewMouseDown="ItemsControl_PreviewMouseDown"
Background="#05FFFFFF">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Stroke="Black" StrokeThickness="2" Fill="Blue"
Height="20" Width="20"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Code Behind:
public partial class ClickToMove : Window
{
public List<MovableObject> Objects { get; set; }
public ClickToMove()
{
InitializeComponent();
Objects = new List<MovableObject>
{
new MovableObject() {X = 100, Y = 100}
};
DataContext = Objects;
}
private void ItemsControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var position = e.GetPosition(this);
Objects.First().MoveToPosition(position.X, position.Y);
}
}
ViewModel:
public class MovableObject: INotifyPropertyChanged
{
private double _x;
public double X
{
get { return _x; }
set
{
_x = value;
OnPropertyChanged("X");
}
}
private double _y;
public double Y
{
get { return _y; }
set
{
_y = value;
OnPropertyChanged("Y");
}
}
private System.Threading.Timer MoveTimer;
private double DestinationX;
private double DestinationY;
public void MoveToPosition(double x, double y)
{
DestinationX = x;
DestinationY = y;
if (MoveTimer != null)
MoveTimer.Dispose();
MoveTimer = new Timer(o => MoveStep(), null, 0, 10);
}
private void MoveStep()
{
if (Math.Abs(X - DestinationX) > 5)
{
if (X < DestinationX)
X = X+5;
else if (X > DestinationX)
X = X-5;
}
if (Math.Abs(Y - DestinationY) > 5)
{
if (Y < DestinationY)
Y = Y + 5;
else if (Y > DestinationY)
Y = Y - 5;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
Application.Current.Dispatcher.BeginInvoke((Action)(() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}

Stylizing graphs and tooltip of DataVisualization.Chart

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>

Categories