Bind Usercontrol contained in a ObservableCollection<CustomClass> in a WrapPanel - wpf

I have a class defined this way:
public class CustomClass
{
string Name;
int Age;
Usercontrol usercontrol;
}
where Usercontrol is a visual element that I want to insert in a WrapPanel.
CustomClass is organized in a static ObservableCollection.
public static class CollectionClass
{
public static ObservableCollection<CustomClass> MyCollection = new ObservableCollection<CustomClass>();
}
I am looking to bind the usercontrol property of the CustomClass in the collection to be visualized in the WrapPanel, so I have the visual elements showed in the same order as the elements in the collection.
Right now I am populating the WrapPanel manually by code, but I figured that there has to be a way to do it quickly and easily through databinding.
I am trying to do it with a ItemsControl defined this way:
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding }">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
but I don't know how to make the magic happen.
EDIT:
I tried the solution proposed by ChrisO, implemented like that:
1 - I made the collection a property
2 - I made the UserControl a property
3 - I set DataContex from code:
SensorMenuWrap.Datacontext = CollectionClass.MyCollection
4 - The binding:
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding }">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding usercontrol}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now I can visualize the first element of the collection. If the first element changes, I visualize the new first element. How can I visualize the entire collection?

I haven't tested this. Also, make sure that userControl is a property rather than a field. I'm assuming you know how to set the DataContext up correctly to refer to the MyCollection property.
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding MyCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding userControl}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You should also consider referring directly to the UserControl in the DataTemplate rather than binding to it as a property on the class. That would look like this
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding MyCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Edit:
In response to your edit, you would be much better tackling this with a reusable UserControl with DataBindings on the properties of CustomClass. Here's what I have to achieve that:
CollectionClass
public static class CollectionClass
{
public static ObservableCollection<CustomClass> MyCollection { get; set; }
static CollectionClass()
{
MyCollection = new ObservableCollection<CustomClass>();
MyCollection.Add(new CustomClass { Age = 25, Name = "Hamma"});
MyCollection.Add(new CustomClass { Age = 32, Name = "ChrisO"});
}
}
CustomClass
public class CustomClass
{
public string Name { get; set; }
public int Age { get; set; }
}
UserControl contents
<StackPanel>
<TextBlock Background="Tan" Text="{Binding Name}" />
<TextBlock Background="Tan" Text="{Binding Age}" />
</StackPanel>
MainWindow
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding }">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<so25728310:Usercontrol Margin="5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here, instead of having the Usercontrol baked into your data, you're keeping it in the view. It's always good to have a separation between your view and data. You could even do away with the Usercontrol and have the two TextBoxes directly in the DataTemplate but it depends on whether you would want to reuse that view elsewhere.

Related

How to put three dots at the end of the StackPanel that will say there are more items

I would like a behavior like in TextBlock that can be given TextTrimming="CharacterEllipsis" and then I will get three dots.
<ItemsControl ItemsSource="{Binding MyList}" MaxWidth="150">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringFormat='{}{0}\, '}" TextTrimming="CharacterEllipsis"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
public ObservableCollection<string> MyList { get; set; } = new() {"AAAAAA", "BBBBBB", "CCCCCC", "DDDDDD" };
And that's my result, but not the result I wanted
Get rid of the ItemsControl and use a TextBlock that binds to a source property that returns the value of string.Join(", ", MyList):
<TextBlock Text="{Binding StringProperty}" TextTrimming="CharacterEllipsis"/>
It doesn't make much sense to try to implement this using an ItemsControl in the view.

Design-time data for ItemsControl's DataTemplate

I want to use some mock data to design my DataTemplate. How do I set a mock ObservableCollection as the ItemsSource of my ItemsControl, considering I'm using d:DataContext on it to point to a mock class containing said collection?
Here is what I have so far:
<DataTemplate x:Key="MyTemplate">
<Grid Margin="5,5,5,5">
<CheckBox Content="{Binding Name}" />
</Grid>
</DataTemplate>
<ItemsControl d:DataContext="{d:DesignInstance Type=mocks:MyViewModelMock, IsDesignTimeCreatable=True}" ItemsSource="{Binding MyMockList}" ItemTemplate="{StaticResource MyTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
public class MyViewModelMock {
public ObservableCollection<MyModel> MyMockList { get; set; }
public MyViewModelMock() {
MyMockList .Add(new MyModel() { Name = "Mock 1" });
MyMockList .Add(new MyModel() { Name = "Mock 2" });
}
}
Point you d:DataContext at a static implementation of that type and you will have your design time data context. Here is a good example http://adamprescott.net/2012/09/12/design-time-data-binding-in-wpf/

Custom Objects in Custom List WPF

I have a viewmodel called Calendar.
in Calendar there is a list of CalendarDaySquare objects. They are displayed in a ItemsControl that is a uniformgrid (from the ItemsPanelTemplate).
On each of these calendarDaySquares I want to populate it with the Events Collection (of CalEvent objects)
public Class Calendar
{
// this is just a list that inherits from List<T>
public CalendarSquareList<CalendarDaySquare> Squares { get; private set; }
}
public class CalendarDaySquare
{
public List<CalEvent> Events {get; private set;}
public ObservableCollection<string> ObsColEvents{get; private set;}
}
Are the same thing, I just tried using the ObservableCollection b/c I've been stumped on this.
Here is my xaml :
<ItemsControl ItemsSource="{Binding Squares}"
Margin="0,0,0,263">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="7" Rows="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.Margin" Value="1"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate xmlns:local="clr-namespace:BudgetCalendarWPF.ViewModel;assembly=BudgetCalendarWPF" DataType="CalendarDaySquare">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--</Button>
</ControlTemplate>
</Button.Template>
</Button>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I hope i've pasted the latest (i've been working on this all day so this is just what i've got in VS currently .. sad face )
Welp, I think i've got this kinda figured out but I'd love any suggestions.
I was trying to hard. I didn't realize it would automatically pick up the item as the type it was. so this worked :
<ListBox ItemsSource="{Binding Events}">
<ListBox.ItemTemplate>
<DataTemplate >
<Button HorizontalAlignment="Stretch">
<TextBlock Text="{Binding Name}"/>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Setting DataContext for binding RelayCommand in xaml

In the following xaml code, I'm trying bind a RelayCommand ResourceButtonClick, which is in the view model. In addition to that, I want to pass the Resource.Id as a parameter to this command.
However, ResourceButtonClick is not called. I suspect that by setting the ItemsSource to Resources, I override the data context, which was view model.
<UserControl ...>
<Grid>
<ItemsControl ItemsSource="{Binding Resources}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Tag="{Binding Id}" Content="{Binding Content}"
Width="300" Height="50"
Command="{Binding ResourceButtonClick}"
CommandParameter="{Binding Id}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
Here's the RelayCommand in the view model.
public RelayCommand<int> ResourceButtonClick { get; private set; }
The constructor of the view model:
public ResourcesViewModel()
{
this.ResourceButtonClick =
new RelayCommand<int>((e) => this.OnResourceButtonClick(e));
}
The method in the view model:
private void OnResourceButtonClick(int suggestionId)
{
...
}
I have two questions: First, how can I call the ResourceButtonClick command. Second, how can I pass Resource.Id as a parameter to that command.
Any suggestion will be appreciated.
Maybe you can show your complete ViewModel class? Assuming, that the ResourceButtonClick command is on the ViewModel which also holds the collection Resources, you're trying to access the command on the wrong object (on the item in Resources, instead of the ViewModel which holds the Resources collection and the command).
Therefore you would have to access the command on the 'other' DataContext, this is the DataContext of the ItemsControl not of it's item. The easiest way is to use the ElementName attribute of the binding:
<UserControl ...>
<Grid>
<ItemsControl ItemsSource="{Binding Resources}" Name="ResourcesItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Tag="{Binding Id}" Content="{Binding Content}"
Width="300" Height="50"
Command="{Binding DataContext.ResourceButtonClick, ElementName=ResourcesItemsControl}"
CommandParameter="{Binding Id}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
Maybe this solves the problem, otherwise please let me know and provide some more detail.
I usually pass the whole item as a CommandParameter, not an ID. This doesn't cost anything and you don't have to translate the ID back to the item. But that depends on your case.
Hope this helps.
I resolved this in a such way:
In View.xaml
1) I added a property SelectedItem for my ListView:
<ListView Name="MyList" ItemsSource="{Binding MyList}" SelectedItem="{Binding MySelectedItem, Mode=TwoWay}" >
2) I added a Command property to a button:
In viewModel:
3) I added a command handler:
MyCommand = new RelayCommand(MyMethod);
4) I added method MyMethod which takes value(s) from MySelectedItem property:
private void MyMethod ()
{
MyType mt = MySelectedItem;
//now you have access to all properties of your item via mt object
}

How can I bind to an element within a list, within a observable collection?

I have the following problem:
I have an ObservableCollection as my model, which looks like this:
public class HistoryViewModel : ViewModelBase
{
private ObservableCollection<List<KeyValuePair<DateTime, float>>> _valuePairs;
public ObservableCollection<List<KeyValuePair<DateTime, float>>> ValuePairs
{
get
{
return this._valuePairs;
}
set
{
this._valuePairs= (ObservableCollection<List<KeyValuePair<DateTime, float>>>)value;
OnPropertyChanged("ValuePairs");
}
}
}
As you can see this collection consists of lists, i need to bind to the KeyValuePair in the list. But i don't know how. The problem is I cannot know how many items will be in the collection nor in the list.
my attempt right now looks like this:
<ItemsControl ItemsSource="{Binding Path=ValuePairs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<charting:Chart Width="400" Height="250">
<charting:Chart.Series>
<charting:LineSeries Title="Monthly Count"
IndependentValueBinding="{Binding //, Path=Key}"
DependentValueBinding="{Binding //, Path=Value}">
</charting:LineSeries>
</charting:Chart.Series>
</charting:Chart>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It displays how much elements are in the collection, but the chart does not show any values.
Any ideas?
Thanks!
Add ItemsSource="{Binding}" to the chart and it should fix it

Resources