Programmatically reference items rendered by ItemsControl from code behind - c#

I have a Silverlight user control that contains an ItemsControl that renders a StackPanel containing another user control for each item in the data source, the XAML is as follows:
<Grid x:Name="LayoutRoot">
<ItemsControl ItemsSource="{Binding}" x:Name="ValuesItemSource">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel x:Name="ValuesPanel" Background="Transparent" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:MyCustomControl DataContext="{Binding}" x:Name="Value" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
How would I reference the collection of MyCustomControls (Values) in the code behind of this user control?
(I have an event handler registered in the code behind of this control and I want to invoke a method of each "MyCustomControl" when the event fires)

You need to ask the itemsControl.ItemContainerGenerator for this. See here for an example.

Related

Itemscontrol with ending control

I'm having a ItemsControl with custom controls wrapped inside. What I want to achieve is to always have a TextBox control along with my custom controls always at the end of WrapPanel.
Here is a slight preview:
Having ItemsControl allows us to have controls of the same type. When TextBoxis added after ItemsControl, it will unfortunately appear on the new line. Therefore I'm forced to somehow append TextBox along with my custom controls in ItemsControl.
<ItemsControl ItemsSource="{Binding MyItems}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<!-- Make items wrap -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Custom control -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CustomControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- This should be inside items control -->
<TextBox />
I did some research and it turns out that I can use DataTemplateSelector class and ItemTemplateSelector which will allow me to choose template for different items.
This is an option of course, however it will require me to insert TextBox into my MyItems (which is ObservableCollection btw.) and I'm not sure if this is the way to go since I will have to locate TextBox and move it at the end of my ObservableCollection, each time a new item is added...
So the question is: what is the proper way to solve this? Looking for experienced developers opinion on this.
You could set the ItemsSource to a CompositeCollection:
<ItemsControl ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ItemsControl.Resources>
<CollectionViewSource x:Key="cvs" Source="{Binding MyItems}" />
</ItemsControl.Resources>
<ItemsControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource cvs}}" />
<!-- This should be inside items control: -->
<TextBox />
</CompositeCollection>
</ItemsControl.ItemsSource>
<!-- Make items wrap -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Custom control -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CustomControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

WPF Draw UserControl based on the Class of the bound object

im working on a menu which can display items.
Therefor i have a UserControl 'MenuItem' which displays an entity of the type 'MenuItemEntity'.
Cause there are submenus which are displayed in a different way there is another UserControl 'MenuItemGroup' which are bound to an entity of type 'MenuItemGroupEntity' containing different MenuItemEntities.
Now i have the following Problem:
The 'menu' should be bound to an entity of the type 'MenuEntity'.
Inside this i want to have an ObservableCollection which contains MenuItemEntity's AND MenuItemGroupEntity which are displayed in an StackPanel using an ItemsControl.
But i dont know if there is any way to analyze the actual element in the bound collection to draw either an MenuItem or an MenuItemGroup. Something like a switch maybe?
Normaly i would bind the items of the ObservableCollection in the 'MenuEntity' like this:
<ItemsControl ItemsSource="{Binding MenuItemAndGroupCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- IS THERE ANY WAY TO SWITCH BASED ON THE CLASS TYPE? -->
<local:MenuItemGroup DataContext="{Binding}" />
<local:MenuItem DataContext="{Binding}" />
<!-- IS THERE ANY WAY TO SWITCH BASED ON THE CLASS TYPE? -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I hope someone can help me out of this =(
Simply use "implicit" DataTemplates, like this:
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyClass1}">
<local:MyUserControl1/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyClass2}">
<local:MyUserControl2/>
</DataTemplate>
<!-- and so on... -->
</Window.Resources>
<ItemsControl ItemsSource="{Binding MenuItemAndGroupCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Do NOT specify an ItemTemplate here -->
</ItemsControl>
</Window>

Binding an ItemsControl containing WindowsFormHost items in WPF

I am building a UI element for our WPF application that allows a user to visualize a collection of graphs aligned in a grid format. As I understand, you can use an ItemsControl with a WrapPanel that will nicely align ui elements in a grid format.
The difficulty comes when we try to use a winforms graphing library (zedgraph) to generate the graphs. This means we have to use WindowsFormsHost to display the graph views. I have tried to bind the graph collection using the normal data binding, it doesn't really work:
XAML:
<ItemsControl ItemsSource="{Binding Graphs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<WindowsFormsHost Margin="5">
<ui:Graph></ui:Graph>
</WindowsFormsHost>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The above code displays nothing, but the Graphs getter is accessed.
Also, even if I cancel the binding and I just insert some random graph views inside the ItemsControl... it still doesn't display anything:
XAML:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<WindowsFormsHost Margin="5">
<ui:Graph></ui:Graph>
</WindowsFormsHost>
<WindowsFormsHost Margin="5">
</WindowsFormsHost>
<ui:Graph></ui:Graph>
</WindowsFormsHost>
<WindowsFormsHost Margin="5">
<ui:Graph></ui:Graph>
</WindowsFormsHost>
</ItemsControl>
To test to make sure the graphing library functions normally, this does indeed display a graph:
<Grid>
<WindowsFormsHost Margin="5">
<ui:Graph></ui:Graph>
</WindowsFormsHost>
</Grid>
Can anyone guide me in the right direction? Much appreciated.
That might be related to layout. winforms is horrible, and requires that you hardcode the sizes of everything.
Try giving a fixed hardcoded Width and Height to the winformshost

An ItemsControl containing UserControl elements

I'm kind of shooting in the dark on this one and have been looking around but couldn't find much of anything related. Pretty much I am trying to create an ItemsControl on a current Window that I've got, so when the user clicks an "Add Product" button on the window, it will add a UserControl to the screen in a horizontal matter.
For starters I am using an MVVM pattern and I have a PricingViewModel which is my ViewModel for the MAIN window. I have a second view model named ComparisonViewModel, which is the ViewModel for the View of the UserControl that I would like to show everytime the user hits the "Add Product" button on the PricingView. Jumping into my code, I've got a declared ObservableCollection and my AddComparison method. The Collection is instantiated in the constructor of the VM.
public ObservableCollection<ComparisonViewModel> Products { get { return _products; } }
public void AddComparison()
{
var products = IoC.Get<ComparisonViewModel>();
Products.Add(products);
}
Next, I've got and ItemsControl in the PricingView that binds to that collection in the PricingViewModel:
<ItemsControl ItemsSource="{Binding Path=Products}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
I run it and after hitting add, it just shows the collection name. How can I actually get it to Pop up with a new Comparison User Control when the user hits Add Comparison? Thanks a ton for the help in advance!
You'll want to set the ItemTemplate so the ItemsControl knows how to render each item in the collection (by default, it is just displaying the result of calling .ToString()).
<ItemsControl ItemsSource="{Binding Path=Products}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type namespace:ComparisonViewModel}">
<!-- EXAMPLE -->
<Border BorderBrush="Black"
BorderThickness="2">
<DockPanel Orientation="Horizontal">
<TextBlock Text="{Binding ComparisonResult}"
DockPanel.Dock="Right" />
<TextBlock Text="{Binding Name}"
DockPanel.Dock="Left" />
</DockPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have found that I needed to tell the ItemsControl two things... First is what type of "thing" the ItemsControl is:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
And the second is how to display the Control:
<ItemsControl.ItemTemplate>
<DataTemplate>
<c:Widget Margin="5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
The final code looks like:
<ItemsControl ItemsSource="{Binding Path=DynamicItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<c:Widget Margin="5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You also need to add a reference to your control namespace in the window declaration stuff:
xmlns:c="clr-namespace:IHateEverything.Controls"

Add more of my usercontrol container or usercontrol collection display

I do not know what the container/control im looking for would be called, so I cannot really search for it.
Add More of Same Usercontrol Usercontrol
Clicking on + would add a new instance of My Usercontrol to the right of the existing ones
Clicking on X would dispose the usercontrol that was clicked
I'm not really looking for a tab control that would put each new instance on a new tab, but if there is nothing else then it might do.
The design is not to be as shown in the image obviously, the image just illustrates the basic idea
Any keyword/name suggestions or links to existing implementations?
e.g. Maybe there is a style that turns a ListBox into something suitable?
I would use an ItemsControl and customize it's ItemsPanelTemplate to be whatever you want.
ItemsControls are meant for iterating through a collection of objects, and displaying them in whatever form you want. I wrote some simple code samples of them here if you're interested, or here's another quick example:
<DockPanel x:Name="RootPanel">
<Button Style="{StaticResource AddButtonStyle}"
DockPanel.Dock="Right" VerticalAlignment="Center"
Command="{Binding AddItemCommand" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding MyCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<local:MyUserControl />
<Button Style="{StaticResource RemoveButtonStyle}"
Command="{Binding ElementName=RootPanel, Path=DataContext.RemoveItemCommand}"
CommandParameter="{Binding }"
HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
Your ItemsControl would be bound to an ObservableCollection of objects, and your Add/Remove buttons would simply add/remove items from that collection. Since it is an ObservableCollection, it will notify the UI when the collection gets changed and automatically update.
You can indeed use a ListBox and set its ItemTemplate and ItemsPanelTemplate:
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Of course, your ItemTemplate would be a reference to your control.
You could look at something called a carousel control which uses a list of objects behind it and displays them similarly to itunes. This could be a bit over the top but is one solution. An example can be seen here
If this is too advanced for your needs, could it be as simple as a stackpanel with a scrollbar which is bound to a list of your user controls?

Categories

Resources