I have a main view whick looks like this:
<ItemsControl Name="Records" />
in ViewModel of this View there's a:
public BindableCollection<RecordViewModel> Records { get; private set; }
and there's also (empty) RecordViewModel and simple RecordView.xaml
In this configuration everything's working fine. The view RecordView shows automatically for every record I add to Records Collection.
But now I'd like to add filtering to it.
So I've created a filter and now need to bind to ICollectionView instead of BindableCollection<RecordViewModel>.
The deal is that Caliburn.Micro doesn't show any view no more, but instead shows MyNamespace.RecordViewModel text in place of where the item's view should be.
How may I fix this?
Ok, I got it
The view should look like this:
<ItemsControl Name="Records">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl cal:View.Model="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It's working now.
But if there's a shorthand for this - let me know.
I used
<ItemsControl.ItemTemplate>
<DataTemplate">
<ContentControl x:Name="Records" />
</DataTemplate>
</ItemsControl.ItemTemplate>
Related
Okay, to start, I'm pretty inexperienced with WPF and XAML, so any pointers or advice would be greatly appreciated.
I have a scheduling program that I'm working on that I need some help setting up. I had things working previously, but it wasn't organized correctly. I had UI elements in my ViewModels that I would add to a StackPanel at the initialization of the MainWindow. Generally not MVVM style coding. So I made some views (UserControls) to display the things I have, and most everything broke.
Basically, I have a Schedule ViewModel that has some parameters and a list of a different Room ViewModels. Each Room ViewModel has a RoomSchedule ViewModel that contains a list of RoomEvent ViewModels.
I'm trying to write controls for the things that need displaying. I've created a Schedule view, which has a list box of Room views, and the Room view uses the RoomEvent view to display the events of the room. The Room view uses the WPF Extended Toolkit's TimelinePanel, the rest of the controls are pretty much basic controls. The general idea has been: a model provides data to the ViewModel, which massages that data to what needs to be displayed. So an Event should know how to display itself, a Room should know how to display itself, and the Schedule should know how to display itself.
The problem I'm running into is: now that I've scooted everything from the xaml.cs or ViewModel files to their appropriate places, the controls aren't rendering at all. I've been reading other SO postings where people have the same problem, but none of them seem to work for beginner stuff like this. I think I'm close, it seems like all the controls are being created, and the DataContext's are being set correctly, but nothing is showing up.
This is, basically, what I have so far. I left some of the xaml boilerplate stuff off for succinctness:
Schedule.xaml:
<StackPanel>
<ListBox ItemsSource="{Binding Rooms}" >
<ListBox.ItemTemplate>
<DataTemplate>
<localcontrols:RoomView ScheduleStart="{Binding ElementName=ScheduleControl, Path=DataContext.Start}"
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
</StackPanel>
RoomView.xaml:
<extended:TimelinePanel BeginDate="{Binding localcontrols:ScheduleStart}" EndDate="{Binding localcontrols:ScheduleEnd}"
<ItemsControl ItemsSource="{Binding Path=mRoomSchedule.mScheduledEvents}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<localcontrols:EventView />
</DataTemplate>
</ItemsControls.ItemTemplate>
</ItemsControl>
</extended:TimelinePanel>
EventView.xaml:
<Border BorderThickness="1" BorderBrush="Black" extended:TimelinePanel.Date="{Binding mStartTime}" extended:TimelinePanel.DateEnd="{Binding mEndTime}">
<TextBlock Background="{Binding mColor}" Text="{Binding mEventID}" />
</Border>
The ScheduleStart and ScheduleEnd are dependency properties defined in RoomView.xaml.cs. My thinking was that Schedule would have Start and End properties that would be set in its constructor, and the RoomViews in the ListBox would bind to those properties to set the TimelinePanel's BeginDate and EndDate.
Maybe your bindings are wrong. When I need to bind to a dependency property I use the ElementName feature of binding to say which control I want and I give the root node a name, in this case Root. It's one way to solve it.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Weingartner.Controls"
x:Class="RoomView"
x:Name="Root">
<extended:TimelinePanel
BeginDate="{Binding ElementName=Root, Path=ScheduleStart}"
EndDate="{Binding ElementName=Root, Path=ScheduleEnd}"
>
<ItemsControl ItemsSource="{Binding Path=mRoomSchedule.mScheduledEvents}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<localcontrols:EventView />
</DataTemplate>
</ItemsControls.ItemTemplate>
</ItemsControl>
</extended:TimelinePanel>
</UserControl>
I wonder to know if it is possible to have an ObservableCollection of ObservableCollections in viewModel like this:
ObservableCollection<ObservableCollection<EditingMetadataViewModel>> MetadatasList = new ObservableCollection<ObservableCollection<EditingMetadataViewModel>>();
each ObservableCollection shows a list of metadata when it is binded to the view. In the case I have more than one file selected I want to have the same metadata lists number as selected files number (e.g if I select three files and I want to edit their metadata, I want to have three lists of metadata list).
Yes you can create a ItemsControl(or Panel controls) of ListView, ListBox, Grid, ... as child items:
<ItemsControl ItemsSource="{Binding MetadatasList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding MetaData}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I don't see why not and would be very easy for you to verify this. You could quickly create a ListBox that has a data template that contains a ListBox.
For clarity however you may want to define a class FileMetadata. In that case the first observable collection will be declared as
public ObservableCollection<FileMetadata> FileMetadataList {get; private set; }
Class FileMetadata would contain a member:
public ObservableCollection<EditingMetadataViewModel>> MetadatasList {get; private set; }
This is equivalent with your code but will may make certain parts easier to read and manage.
I have defined a type Board which containes a public property
ObservableCollection<Column> Columns
I would like to display it with use of MVVM pattern.
I created BoardView and bound it to BoardViewModel. BoardViewModel exposes public property Board of type Board.
BoardView contains a control ItemsControl which sets ItemsSource={Binding Board.Columns}.
<ItemsControl ItemsSource="{Binding Board.Columns}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" Margin="10" BorderBrush="#9f9f9f" Width="250">
<v:BoardColumnView Background="#e3e3e3" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
BoardColumnView should show properties of Column type and this works good.
My problem is that I want to create a ViewModel for BoardColumn, and instead of showing only properties of Column type I want to show BoardColumnViewModel which would have defined inside a Column property.
How can I achieve that?
thanks in advance!
You could simply define a Columns property in your BoardViewModel that would contain a collection of BoardColumnViewModel. Something like this:
public ObservableCollection<BoardColumnViewModel> Columns { get; private set; }
You will need to initialize this property somewhere in BoardViewModel, for example:
public BoardViewModel(...)
{
...
Columns = new ObservableCollection<BoardColumnViewModel>(Board.Columns.Select(c => new BoardColumnViewModel(c)));
}
And then bind to that property, instead of Board.Columns:
<ItemsControl ItemsSource="{Binding Columns}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" Margin="10" BorderBrush="#9f9f9f" Width="250">
<v:BoardColumnView Background="#e3e3e3" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As a general principle in MVVM is that it is not recommended to bind directly to a model. Instead you should always try to bind to a view model. This is why you have the problem you've described - because you bind to a public property Board, which is your model.
In my understanding is BoardColumnView an own UserControl?
If so just set the DataContext of this UserControl with this.DataContext = new BoardColumnViewModel(); in code behind or use XAML equivalent. Then create in your BoardViewModel an ObserveableCollection<> which holds multiple Instances of BoardColumnView and set the ItemSource to this collection. In your BoardColumnView XAML define your Layout, Bindings, etc. which are displayed in BoardView.
So every time you add a BoardColumnView to the ItemsSource, which is bound to the collection, a new instance of BoardColumnViewModel is getting created.
...That is my understanding of your Problem, but I might be completely wrong :).
I'm writing a WPF application. I want it to display data in ListBox from different sources. I want to make some common source interface like
interface IDataSource<T>
{
ObservableCollection<T> Elements { get; set; }
DataTemplate ElementDataTemplate { get; set; }
}
But I don't know which is the best type or types which I should user for IDataSource. I can make it UserControl, but it seems to be unnecessary, because my DataSource is not user control. The main problem is with ElementDataTemplate. How can I properly manage it not from UserControl class? Should I care another helper UserCntrol class and call something like (new MyUserControl).FindResource("ElementsDataTemplate") to obtain datatemplate or there is more fine way to keep and get DataTemplate?
You can simply apply a data template for a specific type in the resource section of the corresponding view:
<!-- Items may be of type ViewModel1 and ViewModel2 -->
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type vm:ViewModel1}">
<TextBlock Text="{Binding PropertyA}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ViewModel2}">
<TextBlock Text="{Binding PropertyB}" />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
So there is no need for the interface.
I'm new to WPF and using MVVM. I have a view in which I want to display different content according to what a user selects on a menu. One of those things is another user control Temp which has a view model (TempVM) so I am doing this:
<ContentControl Content="{Binding Path=TempVM}"/>
and TempVM (of type TempViewModel)is null until the user clicks a button. Its data template is this
<DataTemplate DataType="{x:Type vm:TempViewModel}">
<view:Temp />
</DataTemplate>
That's fine, but the other thing I want to do is show a listbox when a user clicks a different menu item. So I am trying to do
<ContentControl Content="{Binding Path=Missions}"/>
(Missions is an observable collection of MissionData) and trying to template it like this:
<DataTemplate DataType="{x:Type ObservableCollection(MissionData)}">
<StackPanel>
<ListBox ItemsSource="{Binding}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go"/>
</StackPanel>
</DataTemplate>
But the compiler doesn't like the type reference. If I try doing it by giving the template a key and specifying that key in the ContentControl it works but obviously I see the ListBox and button when there's no Missions. Obviously I could make a user control and viewmodel and follow the same pattern as I did for the TempVM but it seems over the top. Am I going the right way about this and what do I need to do?
From what i see is that you try to use a Collection as a dataobject which is in my opinion bad practice. Having a DataTemplate for a collection is also problematic, like you already have witnessed. I would advice you to use a ViewModel for your missions collection.
class MissionsSelectionViewModel
{
public ObservableCollection<Mission> Misssions;
public MissionData SelectedMission;
public ICommand MissionSelected;
}
and modify your datatemplate to
<DataTemplate DataType="{x:Type MissionsSelectionViewModel}">
<StackPanel>
<ListBox ItemsSource="{Binding Missions}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go" Command="{Binding MissionSelected}/>
</StackPanel>
</DataTemplate>
If I were to follow your pattern of implicit templates, I would derive a custom non-generic collection MissionDataCollection from ObservableCollection<MissionData> and use it to keep MissionData items. Then I would simply reference that collection in DataType. This solution gives other advantages like events aggregation over the collection that are useful.
However, it seems to me that the best solution is the following.
Add a IsMissionsListVisible property to your VM.
Bind the Visibility property of the ContentControl showing the list to the IsMissionsListVisible property.
Use a keyed DataTemplate resource.
Implement the logic that determines if IsMissionsListVisible. Supposedly it should be true when there is at least one mission in the selected item. But the logic may be more complex.
I would do it this way. In fact, I do it this way usually, and it gives several benefits. The most important is that I can explicitly control the logic of content visibility in various situations (e.g. async content refresh).