Reputation: 192
I am trying to data bind a collection of lines, and perform a sort function on them and update the UI once the sort has been completed (would like to show the differences in sort algorithms).
I have a basic WPF application which consists of an ItemsControl which is bound to a collection of objects. These objects are bound correctly when the screen is first rendered, however once the sort operation has been completed, the underlying list has been sorted correctly, but the UI has not been redrawn?
Here is my XAML
<Grid>
<Button Content="Sort" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="12" MinWidth="80" Click="Button_Click"/>
<ItemsControl x:Name="mainControl" ItemsSource="{Binding Values}" ItemTemplate="{StaticResource LineDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter" />
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
there is a xaml data template
<DataTemplate x:Key="LineDataTemplate">
<Line X1="{Binding X1}" Y1="{Binding Y1}"
X2="{Binding X2}" Y2="{Binding Y2}"
Stroke="DarkGray" StrokeThickness="3"/>
</DataTemplate>
The main data context contains a list of this Line object
public class Line
{
public int X1 { get; set; }
public int Y1 { get; set; }
public int X2 { get; set; }
public int Y2 { get; set; }
}
And when the datacontext is initialised I create some random lines
private void RandomiseLines()
{
var rnd = new Random();
var startingPoint = 2;
Values = new List<Line>();
for (int i = 0; i < 3; i++)
{
Values.Add(new Line() { X1 = startingPoint, Y1 = 420, X2 = startingPoint, Y2 = (420 - rnd.Next(1, 300)) });
startingPoint += 4;
}
}
Then I have a button on the UI which calls through and (for now) calls a basic sort using linq
Values = Values.OrderBy(x => x.Y2).ToList();
The data context, where this list is held implements the INotifiedProperty changed interface, and once the list is sorted I make a call to the Property changed event. Although the underlying list get sorted the UI does not seem to be redrawing, I have tried using an ObservableCollection and wrapping in Dispatcher but I do not seem to have any binding errors or exceptions being thrown. Can anyone please explain why this does not get updated?
Edit: Added expected result The expected result would be the ItemsControl redrawing itself and the lines would be in the new sorted order
Upvotes: 1
Views: 401
Reputation: 29028
You better use Rectangle
instead of Line
, because it doesn't rely on coordinates for positioning. You just give them a shared Width
but a variable Hight
. The ItemsPanel
should be a StackPanel
with the StackPanel.Orientation
set to Horizontal
. The Value collection must be a ObservableCollection<double>
. Then it should behave as expected.
This way the order of the bars will reflect the order of the collection.
The main view
<StackPanel>
<Button Content="Sort"
Click="Button_Click" />
<ItemsControl x:Name="mainControl"
ItemsSource="{Binding Values}"
ItemTemplate="{DynamicResource LineDataTemplate}">
<ItemsControl.Resources>
<DataTemplate x:Key="LineDataTemplate" DataType="system:Double">
<Rectangle Width="5"
Height="{Binding}"
VerticalAlignment="Bottom"
Fill="DarkGray"
Margin="0,0,3,0" />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
The Button.Click
event handler
private void Button_Click(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as TestViewModel;
var orderedValues = viewModel.Values.OrderBy(value => value).ToList();
viewModel.Values = new ObservableCollection<double>(orderedValues);
}
The view model
private void RandomiseLines()
{
var rnd = new Random();
Values = new ObservableCollection<double>();
for (int i = 0; i < 3; i++)
{
Values.Add(rnd.Next(1, 300));
}
}
Upvotes: 1