From Code Retrieve dynamically generated objects from listview's datatemplate - c#

The key here in the title is that I want to retrieve an object, specifically a canvas, that is contained in my listviewitem's datatemplate.
I have an ObservableCollection of ints in the ViewModel property MySimpleData. Associated with each int is a canvas. Essentially I am using a listview to display an array of "pictures". The user can click buttons which changes the contents of the "Canvases". However a single picture object can last many frames so I don't store them in the canvas but rather in a separate location with a start index and a duration. I would like to procedurally generate each of the canvases in my listview. How can I retrieve my canvas for each index?
I am looking for soemthing along the lines of:
MyListView.Items.(Related-DataTemplate).(Related-Canvas)
My goal is to essentially clear all the canvases and re-draw/refresh them when I want. This is a mock up / demo so I don't mind if the solution is a bit hacky. I just need something that works well enough and won't require me to write my own control.
My intent would be to iterate MyListView.items essentially call, related-canvas.clear(); and then for the picture-objects for that canvas I will call related-canvas.addChild(Relevant-Picture-Object);
Here is my xaml in case it helps.
<ListView Name="MyListView"
ItemsSource="{Binding MySimpleData}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Column1"
DisplayMemberBinding="{Binding}"/>
<GridViewColumn Header="Column2-Canvases"
Width="{Binding DataContext.CanvasWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Canvas
Width="{Binding DataContext.CanvasWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Height="{Binding DataContext.CanvasHeight, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Background="LightSlateGray"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
Any ideas would are greatly appreciated.
Cheers.

So I ended up replacing the collection with a collection of canvas objects. The next step was to replace the binding and use a content presenter, to directly display the canvases that were in my collection. One other useful change I made was to the column 1 code to display the index of my items. Not as hacky as I was looking for, but arguably hacky enough.
<ListView Name="MyListView"
<!-- COMMENT ItemsSource="{Binding MySimpleData}"> END COMMENT-->
ItemsSource="{Binding CanvasCollection}"> <!-- EDIT HERE -->
AlternationCount="{Binding CanvasCollection.Count}"
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.View>
<GridView>
<GridView.Columns>
<!-- COMMENT <GridViewColumn Header="Column1"
DisplayMemberBinding="{Binding}"/> END COMMENT -->
<GridViewColumn Header="Index" Width="37">
<GridViewColumn.CellTemplate> <!-- NEW Index displaying code -->
<DataTemplate>
<Label Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}, Path=(ItemsControl.AlternationIndex)}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Column2-Canvases"
Width="{Binding DataContext.CanvasWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<!-- COMMENT <Canvas
Width="{Binding DataContext.CanvasWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Height="{Binding DataContext.CanvasHeight, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Background="LightSlateGray"/> END COMMENT -->
<ContentPresenter Content="{Binding}" /> <!-- NEW -->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>

Related

Select first item in ListView by default, MVVM, C#

I am writing some programm where is possible to select device and later configure it. I implemented ListView where all device are listed. I like that first device is selected by default. How to do that? I try several solution found on StackOverflow and over Google but without luck.
Here is my code in XAML:
<ListView Name="lvdevices" Grid.Row="1" Margin="2" ItemsSource="{Binding devicelist}" SelectionMode="Single" SelectedItem="{Binding SelectedDevice}" DataContext="{Binding }">
<ListView.View>
<GridView x:Name="gridDevices">
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Tag="{Binding ID}" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}, Path=IsSelected}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="DeviceId" Header="DeviceId" DisplayMemberBinding="{Binding DeviceId}" Width="50"/>
<GridViewColumn x:Name="NameId" Header="NameId" DisplayMemberBinding="{Binding NameId}" Width="100"/>
<GridViewColumn x:Name="ManufacturerId" Header="ManufacturerId" DisplayMemberBinding="{Binding ManufacturerId}" Width="150"/>
</GridView>
</ListView.View>
</ListView>
I dont know how to implement that first item is selected by default when is connected. It is also possibility that no device will found, what then? Please for help! If and question please ask.
When you populate your devicelist, you can set the SelectedDevice to be the first item in the device list, or null if there is nothing in the list.
using System.Linq;
...
SelectedDevice = devicelist.FirstOrDefault();
Assuming that SelectedDevice implements INotifyPropertyChanged, this should then select the first item on your view.
In your ViewModel, you just need to set your SelectedDevice to the first item (and of course trigger a OnPropertyChanged)

WPF ListView - 2 dimensions with 1 same structure

We are working on a ListView (C# WPF) and we didn't found how bind a list of items in columns, with each item containing itself a list of items with the same columns.
Let's illustre this in an example :
!
We got an observable collection on objects with parameters (name, etc.) and each object contain another observable collection of objects with the sames parameters (exept they haven't a list). So we want to list it in a ListView but we can't figure how !
We do not know enough ListView to implement this structure, some advices ?
Thanks in advance
I think the best way is :
XAML :
<Window.Resources>
<DataTemplate x:Key="gridViewSecondCellTemplate1">
<StackPanel Width="100">
<TextBlock Text="{Binding Content}" FontSize="15" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="gridViewCellTemplate1">
<StackPanel Width="100">
<TextBlock Text="{Binding Title}" FontSize="15" />
<ListView ItemsSource="{Binding MySecondSource}>
<ListView.View>
<GridView>
<GridViewColumn Header="{Binding Subtitle}" CellTemplate="{StaticResource gridViewSecondCellTemplate1}"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</DataTemplate>
</Window.Resources>
<ListView ItemsSource="{Binding MySource}">
<ListView.View>
<GridView>
<GridViewColumn Header="Col 1" Width="100" CellTemplate="{StaticResource gridViewCellTemplate1}"/>
</GridView>
</ListView.View>
</ListView>
I didn't try this code. Try it, and say me if it is OK.

Split items using ItemsControl

This is the XAML:
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Path=ScenarioName}" Header="Scenario Name" />
<GridViewColumn DisplayMemberBinding="{Binding Path=ScenarioType}" Header="Scenario Type" />
<GridViewColumn Header="Well names" Width="175">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=Wells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<!--<TextBlock Text="{Binding}"/>-->
<Button Width="60" Margin="0 0 0 3">Test 1</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=StartPeriod}" Header="Start period" />
<GridViewColumn DisplayMemberBinding="{Binding Path=EndPeriod}" Header="End period" />
<GridViewColumn DisplayMemberBinding="{Binding Path=Lagged}" Header="Lagged" />
<GridViewColumn DisplayMemberBinding="{Binding Path=Detrended}" Header="Detrended" />
<GridViewColumn DisplayMemberBinding="{Binding Path=MinimumMonths}" Header="Length of continuous months" />
<GridViewColumn DisplayMemberBinding="{Binding Path=CorrelatedWells}" Header="Correlated wells" />
<GridViewColumn Header="Excluded Wells">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=ExcludedWells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
And it looks like:
http://tinypic.com/view.php?pic=xpse8n&s=6
The questions is whether there is an easy to group the "Test1" buttons under Well names(in groups of,let say, 5 items) because the volume of well names can be quite big and neither horizontal nor vertical wrapping helps.
many thanks,
elias
Well you can always add a new property to your ViewModel
public IEnumerable<Well> SomeWells
{ get { return Wells.Take(5); }
and change your ItemsSource binding to use this instead.
Example here on MSDN
You could use a ListBox to let the user scroll through the buttons like:
<GridViewColumn Header="Well names" Width="175">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Button></Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
I'm not 100% sure of what you're asking, but it sounds like you want something like 5 buttons in a row, and automatically wrapping to the next row every 5 buttons.
To make it work with a WrapPanel, you should give your panel a Width that is 5x with width of your buttons so it will draw 5 of them, then wrap to the next line
<ItemsPanelTemplate>
<WrapPanel Width="300" Orientation="Vertical"/>
</ItemsPanelTemplate>
Another alternative if you don't know your Button width ahead of time is to use a UniformGrid with 5 Columns. I think you also need to set the Row count to something larger than you ever expect your collection to be
<ItemsPanelTemplate>
<UniformGrid Columns="5" Rows="100" />
</ItemsPanelTemplate>
Another alternative for defining the Row count is to expose a property in your data model that contains the number of rows you'll need, and binding the Rows property to it.
<ItemsPanelTemplate>
<UniformGrid Columns="5" Rows="{Binding RowCount}" />
</ItemsPanelTemplate>
I can't remember if UniformGrid.Rows is a DependencyProperty or not, but if not then you can create an AttachedProperty instead that you can bind to and that will modify the UniformGrid.Rows property (for an example, see this code for Attached Properties for a Grid's Rows and Columns)

Checkbox in gridviewcolumn(header)

I am using a listview (gridview/gridviewcolumn)
where the first column
contains only checkboxes for each row. Instead of adding a select all button I want
to add a Checkbox into the header of the first column.
Selecting the checkbox in the header will select
all other checkboxes and vice versa.
How can I do that in xaml?
Update:
This is the important part of my xaml code. (simplified)
<ListView ItemSource="...">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
To have a checkbox on top of GridViewColumn you can do something like this
<GridViewColumn>
<GridViewColumn.Header>
<CheckBox/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Now you can handle the check event and in the code you can iterate through your itemsSource and change values or if you are following MVVM you can bind property with checkbox so that whenever check is changed you will be notified through INotifyPropertyChanged. Once you find out through binding that check has changed, again you can change your itemssource accordingly

How to bind to 2 different members in a class in WPF?

I have a class like:
class EditorViewModel
{
public ObservableCollection<Effect> AllEffects;
public bool HasPermissions;
}
But the problem is, when I am trying to bind AllEffects to ListView, then I can't bind anything to HasPermissions because the binding scope is limited to AllEffects, not EditorViewModel.
I tried this but it doesn't work:
<ListView ItemsSource="{Binding EditorViewModel}">
...
<GridViewColumn Width="Auto" Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding AllEffects.Name}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="Auto" Header="Type">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding AllEffects.Type}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
If I set the ItemsSource to EditorViewModel and get rid of AllEffects, it works. But then I don't know how to access HasPermissions through binding:
<GridViewColumn Width="50" Header="Override">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="0"
HorizontalAlignment="Center"
IsEnabled="{Binding HasPermission}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
As I updated my answer on this question to include, you can bind the ListView to the AllEffects property of your ViewModel and then refer to a different property of the ViewModel using a relative binding. So assuming your ListView is contained in a Window whose DataContext is an EditorViewModel, and the ListView's ItemsSource is AllEvents, you can still reference HasPermission like so:
<CheckBox Margin="0"
HorizontalAlignment="Center"
IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.HasPermission}"/>
That somewhat clunky notation will find the nearest parent element to the CheckBox in the visual tree that is of type Window, and bind to its DataContext property to find HasPermission.
A classic trick is to use ViewModelLocator, see:
MVVM Light - using ViewModelLocator - properties hit multiple times
also, for a more quick-and-dirty solution, you can use the following Binding:
{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}, Path=DataContext.HasPermissions}
Note that this would only work on WPF and not in SL, since SL doesn't support this syntax of RelativeSource.

Categories