Is there any means to set Canvas Children Property without ItemsControl ItemsSource Binding Property?
In order to keep separated my view from viewmodel, I have to bind the items.
I have used the the canvas as a designer from 'CodeProject'
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part2.aspx
I'm using a canvas for drag-and-drop purposes. It works well when I work manually inside the canvas.
Which means I add and remove the child items using
myCanvas.Children.Add(userControl);
myCanvas.Children.Remove(userControl);
But if I load my usercontrols at run time, they are loaded just as views.
<s:Canvas AllowDrop="True" >
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=userControls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<s:Canvas Background="Transparent"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<s:ControlItem Content="{Binding Path=MyView}"></s:ControlItem >
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</s:Canvas>
No, there aint. (Except manually clearing and adding...)
Ummm yeah just draw items inside the canvas? :)
<Canvas>
<TextBlock Text="I'm Child #1" />
<TextBlock Text="I'm Child #2" Canvas.Top="50" />
</Canvas>
Or you can always do it in code-behind
myCanvas.Children.Add(myTextBlock);
foreach(var someControl in SomeControlList)
myCanvas.Children.Add(someControl);
Edit
I see your update and have no idea what you're asking. If you want to drag/drop items onto a Canvas, you are better off adding/removing items from the ItemsSource than manually adding/removing items from the Canvas. Simply adding/removing them from myCanvas will not update the collection in your ItemsSource
I would recommend taking a look at Bea Stollnitz's article on dragging/dropping databound Items. This means you would keep the ItemsControl you have now, but when you drop an item on top of the Canvas it adds the DataItem behind that object to the ObservableCollection<MyDataItem> that you call userControls (I don't like this name because it suggests that the data items contain UI items, which should not be the case)
Related
If I have a class called: GuiObject, and that class has a list of GuiObjects called: "GuiObjects".
Now say my window has a list of GuiObjects, which I use in the .xaml file to dataBind to:
<StackPanel>
<ItemsControl ItemsSource="{Binding TopObjectList}" DataTemplateSelector="{DynamicResource templateSelector"/>
</StackPanel>
I can make a datatemplate for every type of FrameworkElement I want to generate, but I'm having trouble with the TabControl. I can create a datatemplate for the tabControl like so:
<DataTemplate x:key="TabControlTemplate" DataTemplateSelector="{DynamicResource templateSelector" >
<TabControl ItemsSource="{Binding GuiObjects}" />
</DataTemplate>
And the result is a tab control that has each of the proper pages present, but without the contents of the individual TabItems. Fair enough, I'll just make a DataTemplate for the TabItems. For each TabItem, I'd like to put the contents of GuiObjects into a stackpanel.
<DataTemplate x:key="TabItemTemplate" DataTemplateSelector="{Resource templateSelector">
<TabItem Header = {Binding Title}>
<StackPanel>
<ItemsControl ItemsSource="{Binding GuiObjects}" DataTemplateSelector="{DynamicResource templateSelector"/>
</StackPanel>
</TabItem>
</DataTemplate>
The problem here is that the TabItemTemplate never gets called. I've tried solutions that involve setting the ItemContainerStyle within the TabControlTemplate, but then I've got the problem of hierarchy. If I bind "GuiObjects" inside the content of the TabItem, I'm binding the list of tabItems, instead of the list that's within each TabItem. (I want to do the second one). Here's an example:
<DataTemplate x:key="TabControlTemplate" DataTemplateSelector="{DynamicResource templateSelector" >
<TabControl ItemsSource="{Binding GuiObjects}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Title}"/>
<Setter Property="Content" Value="<StackPanel><ItemsControl ItemsSource="{Binding GuiObjects}" DataTemplateSelector="{DynamicResource templateSelector"/></StackPanel>"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
</DataTemplate>
Again, this solution has the levels problem: When I say: {Binding GuiObjets} I'm referring to the list of TabItems, instead of to the list of FrameworkElements within each TabItem.
The solution is either to stick with separate DataTemplates for both the TabControl and the TabItem, and just fix it so that the DataTemplateSelector actually works for the TabItems (no idea how to do this). Or to go with the ItemContainerStyle, and somehow tell it to go down one level when binding GuiObjects. Anyone know how to do this?
To provide a template for the contents of the pages of a TabControl, use the following properties:
ContentTemplate
ContentTemplateSelector
The ItemTemplate/ItemTemplateSelector properties of a TabControl are used to define what the tab headers look like.
I have a ListBox that I populate dynamically via a binding (this is defined in a DataTemplate, which is why the binding is somewhat unusual):
<ListBox SelectionMode="Extended" ItemsSource="{Binding DataContext.ResultList, RelativeSource={RelativeSource AncestorType=Window}}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Object}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Each ListBoxItem's IsSelected property is bound to an IsSelected property on a custom object.
When I select individual ListBoxItems, the binding works properly - the custom object's IsSelected property is updated in my ViewModel. However, if I select all of the ListBoxItems with a Ctrl+A command, only the currently visible ListBoxItems (those that are currently in my scrolling viewport) update their ViewModel bindings. On the frontend, all the ListBoxItems appear to be selected, and the ListBox.SelectedItems.Count property on the container ListBox shows that all items are selected.
Furthermore, as I scroll through the ListBox after selecting all ListBoxItems with Ctrl+A, the bindings are successfully updated when each ListBoxItem is scrolled into view.
Why does this binding seem to be only partially working? Is there a better way to handle the binding of the IsSelected property when large numbers of ListBoxItems can be selected simultaneously?
Edit:
This behavior doesn't happen exclusively with the Ctrl+A command - I get the same results when selecting all the items using a shift+click.
I think the behavior you're seeing is to due to VirtualizingStackPanel.IsVirtualizing which is True by default when binding to ItemsSource of ListBox
if you for eg set your ListBox such as:
<ListBox VirtualizingStackPanel.IsVirtualizing="False" SelectionMode="Extended" ItemsSource="{Binding DataContext.ResultList, RelativeSource={RelativeSource AncestorType=Window}}">
or
<ListBox ...>
...
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
then you should see all your bound items have their IsSelected updated accordingly with Ctrl+A or Shift + ...
Properties such as Count of the collection even with virtualization would report the correct value to accommodate for things like computing the required ScrollBar.Height. Items which are outside the View-port do not get rendered hence no bindings are in effect on them until they actually get used.
I want to archive a vertical ListView that contains a UserControl in which i have some infos on the left and a second ListView that will scroll horizontal.
The problem is that my second listview will not start to scroll. It seems to pick unlimited space.
First of all some more infos about my setup:
My window is arranged via a grid basically just a row on top with text and the first listview in the second row.
The first listview uses a ItemTamplate like the following:
<ListView.ItemTemplate>
<DataTemplate>
<controls:MyControl />
</DataTemplate>
</ListView.ItemTemplate>
Beside that it has a ItemContainerStyle to display a horizontal line between the items:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem"
BasedOn="{StaticResource {x:Type ListViewItem}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<StackPanel>
<Rectangle x:Name="Separator"
Height="2"
SnapsToDevicePixels="True"
Fill="{DynamicResource AccentColorBrush}" />
<ContentPresenter />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
(trigger that disables the unnecessary first line omitted)
MyControl is a UserControl which is also just a grid first column some text and second column is my second listview.
This one is special because it is horizontal. Since their are various approaches for this from arround the internet i will show the one i ended using with.
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"
IsItemsHost="True"
IsVirtualizing="True"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
Item template is again a UserControl (at the moment just a TextBlock) and again the separator lines but this time vertically.
The problem is now that the second ListView is not scrollable. Even if i explicit set them to be visible they are disabled (also after resizing window).
My approach solving this was binding MaxWidth of Stackpanel in ListViewItem Template to Actual with of my MetroWindow.
MaxWidth="{Binding Path=ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type metroControls:MetroWindow}}}"
But this and some other tries binding different sizes of different items does not work.
Can somebody give a hint to resolve this?
I have a drop shadow style for a StackPanel. The content is bound to an ObservableCollection and each item is rendered as an Image. When I remove the last item from the collection, the drop shadow remains, even though the image is no longer there. This only happens with the last item.
What's going on? How do I ensure the shadow is removed too?
<Style TargetType="StackPanel" x:Key="ShadowedStackPanel">
<Setter Property="BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect Color="Black" Direction="50" ShadowDepth="5" Opacity=".65" Softness="1"/>
</Setter.Value>
</Setter>
</Style>
<ItemsControl x:Name="TilesControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Style="{StaticResource ShadowedStackPanel}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Path=ImageSource}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The codebehind is basically:
public ObservableCollection<TileViewModel> Hand = new ObservableCollection<TileViewModel>();
TilesControl.ItemsSource = Hand;
// populate the collection here
var last = Hand.Last();
Hand.Remove( last );
And this leaves a lingering dropshadow effect.
If I remove any other earlier item in the collection, it redraws everything fine. This only happens if I remove the last item.
If I resize the window, it gets redrawn and fixed... but for whatever reason, it leaves a lingering shadow if I don't.
How do I force the redraw so the shadow goes away? Or avoid the problem in the first place?
You could use the code after removing
TilesControl.Items.Refresh();
I was wondering if its possible to somehow bind a list of lists to a cavas panel. For example having object "layer" which itself is a list of "rectangle" objects. Would if be possible to bind a list of layers to a canvas? So far I can only do it by binding a flattened INumerable of the nested list (using SelectMany function) to an itemcontrol, but this isnt good enough, Id like to keep the "layers" separate and have the Zindex of the rectangles vary according to the layer it is at, allowing to reorder the layers easily.
I have also tried having nested itemcontrols but as expected it only displays the first layer. The aim is to draw a rectangle for every object in every layer on the canvas allowing for layer manipulation, insertion of new objects in each layer, etc...
Thanks in advance! :)
How about making the ItemTemplate of the main ItemsControl another ItemsControl with yet another Canvas as ItemsPanel? (I do not see a reason why it should only display the first layer)
Actually the main ItemsControl does not need to be a Canvas, a Grid would do as well (at least unless your collections have coordinates of their own or a Z-Order, if you use a grid the layer order is he same as their appearance in the collection).
An example (a bit verbose but can't do anything about that):
<ItemsControl ItemsSource="{Binding Data}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding LayerItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<!-- Here would the binding to some properties that take care of placement -->
<Setter Property="Canvas.Top" Value="{Binding Top}" />
<Setter Property="Canvas.Left" Value="{Binding Left}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<!-- Some template for the individual items -->
<DataTemplate>
<Border Padding="5" BorderThickness="1" BorderBrush="Red" CornerRadius="3"
Background="White">
<TextBlock Text="{Binding Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Like H.B said, i too can't see a reason why it will not work. Having an ItemsControl in another ItemsContainer works perfectly fine.
<ItemsControl ItemsSource="{Binding Layers}">
<ItemsControl.ItemsContainerStyle>
<Style>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ItemsControl ItemsSource="{Binding Rectangles}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ItemsControl.ItemsContainerStyle>
</ItemsControl>
But it think it would be cleaner to put the sub ItemsControl inside an DataTemplate for your Items, without the need to modify the ItemsContainerStyle. I think its bad design if your control needs to know informations about the properties of the model, which is necessary for binding to Rectangles.