I am trying to learn MVVM style updates. Not so smoothly.
I am stuck in updating a simple rectangle that is drawn based on a change in the point collection. When the user interface update is initialized, but with a simple change to the point collection, Path is not updated in the user interface.
I added some TextBlocks to make sure that the Change event was fired, but I lost a bit at this point.
Any help appreciated:
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ExampleGreg" x:Class="ExampleGreg.MainWindow"
Title="MainWindow" Height="161.614" Width="324.087">
<Grid x:Name="gridUser" MouseDown="click_MouseDown" >
<Canvas x:Name="MeterCanvas" Margin="14,7,30,0" Background="#FFAFAFAF" Height="35" VerticalAlignment="Top">
<Path Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry x:Name="geometry"/>
</Path.Data>
</Path>
<Path Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry x:Name="polylinePwr">
<PathGeometry.Transform>
<ScaleTransform ScaleX="{Binding ActualWidth, ElementName=MeterCanvas}" ScaleY="{Binding ActualHeight, ElementName=MeterCanvas}" />
</PathGeometry.Transform>
<PathGeometry.Figures>
<PathFigure IsClosed ="True" StartPoint="{Binding Path=thePoints[0]}">
<PathFigure.Segments>
<PathSegmentCollection>
<PolyLineSegment Points="{Binding thePoints, UpdateSourceTrigger=PropertyChanged}" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
<Label Content="{Binding thePoints[0]}" Margin="14,58,199.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
<Label Content="{Binding thePoints[1]}" Margin="14,88,199.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
<Label Content="{Binding thePoints[2]}" Margin="165,58,29.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
<Label Content="{Binding thePoints[3]}" Margin="165,93,29.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
</Grid>
</Window>
MainWindow:
public partial class MainWindow : Window
{
private MainViewModel mvm;
public MainWindow()
{
InitializeComponent();
mvm = new MainViewModel();
this.DataContext = mvm;
}
private void click_MouseDown(object sender, MouseButtonEventArgs e)
{
mvm.theText = mvm.theText + ".";
mvm.ChangePoint(.4);
}
}
ModelView:
class MainViewModel : INotifyPropertyChanged
{
private string _theText = "Initial";
private PointCollection _points = new PointCollection();
private PolyLineSegment segment;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
public MainViewModel()
{
ChangePoint(0.9);
}
public string theText
{
get { return _theText; }
set
{
if (_theText != value)
{
_theText = value;
OnPropertyChanged("theText");
}
}
}
public PointCollection thePoints
{
get
{ return _points; }
}
public void ChangePoint(double x)
{
_points.Clear();
AddPoint(new Point(0.2, 0.2));
AddPoint(new Point(0.2, 0.8));
AddPoint(new Point(x, 0.8));
AddPoint(new Point(x, 0.2));
OnPropertyChanged("thePoints");
_theText = _theText + "!";
OnPropertyChanged("theText");
}
public void AddPoint(Point p)
{
_points.Add(p);
}
}
Thanks for any suggestions (which work :))
- CHANGE ANSWER BELOW -
I added the IValueConverter class:
public class PointCollectionConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value.GetType() == typeof(ObservableCollection<Point>) && targetType == typeof(PointCollection))
{
var pointCollection = new PointCollection();
foreach (var point in value as ObservableCollection<Point>)
pointCollection.Add(point);
return pointCollection;
}
return null;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
And modified ModelView to use the Observable collection ... Updated ModelView:
class MainViewModel : INotifyPropertyChanged
{
private string _theText = "Initial";
private ObservableCollection<Point> _points;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
public MainViewModel()
{
_points = new ObservableCollection<Point>();
_points.Add(new Point(0.2, 0.2));
_points.Add(new Point(0.2, 0.8));
}
public string theText
{
get { return _theText; }
set
{
if (_theText != value)
{
_theText = value;
OnPropertyChanged("theText");
}
}
}
public ObservableCollection<Point> thePoints
{
get
{ return _points; }
}
double xAdder = 0;
double y = 0.0;
public void ChangePoint(double x)
{
y = y + .1;
if (y > .9) { y = .1; xAdder += .1; }
_points.Add(new Point(x+xAdder, y));
OnPropertyChanged("thePoints");
_theText = _theText + "!";
OnPropertyChanged("theText");
}
}
And updated XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ExampleGreg" x:Class="ExampleGreg.MainWindow"
Title="MainWindow" Height="161.614" Width="324.087">
<Window.Resources>
<local:PointCollectionConverter x:Key="pointCollectionConverter"/>
</Window.Resources>
<Grid x:Name="gridUser" MouseDown="click_MouseDown" >
<Canvas x:Name="MeterCanvas" Margin="14,7,30,0" Background="#FFAFAFAF" Height="35" VerticalAlignment="Top">
<Path Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry x:Name="geometry"/>
</Path.Data>
</Path>
<Path Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry x:Name="polylinePwr">
<PathGeometry.Transform>
<ScaleTransform ScaleX="{Binding ActualWidth, ElementName=MeterCanvas}" ScaleY="{Binding ActualHeight, ElementName=MeterCanvas}" />
</PathGeometry.Transform>
<PathGeometry.Figures>
<PathFigure StartPoint="{Binding Path=thePoints[0], Converter={StaticResource pointCollectionConverter}}">
<PathFigure.Segments>
<PathSegmentCollection>
<PolyLineSegment Points="{Binding thePoints, Converter={StaticResource pointCollectionConverter}}" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
<Label Content="{Binding thePoints[0]}" Margin="14,58,199.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
<Label Content="{Binding thePoints[1]}" Margin="14,88,199.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
<Label Content="{Binding thePoints[2]}" Margin="165,58,29.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
<Label Content="{Binding thePoints[3]}" Margin="165,93,29.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
</Grid>
, , , IValueConverter PointCollection. ObservableCollection .
, . , - .
№ 2. Observable IValueConverter PointCollection PointCollection :
public void AddPoint2(Point pt)
{
PointCollection pc = new PointCollection(_points2);
pc.Add(new Point(pt.X, pt.Y));
_points2 = pc;
OnPropertyChanged("thePoints2");
}
foreach (Point pt in sinWave) mvm.AddPoint2(pt);
x++;
, .