I am currently customizing a ListBox. With that I mean adding an image-element, second line-element etc. to one listItem.
The itemSource is a List in C#, therefore I have no preview of the items in Expression Blend/VS. And that's the pain.
Because I have always to edit the XAML and then deploy to check. And this goes on and on until the last pixel is correct.
Isn't there a way, of editing a ListBox with custom items (with a dynamic itemSource) live in Blend/VS?
That would really fasten up my developing.
If you want to see how your controls look like in design time, you must use SampleData. There are several ways to do it, it depends on your framework.
Let's say you have a page named MainPage.xaml. If you don't have view model already, create a new one and name it MainViewModel.cs. Define all public properties that will be used for binding.
Once you have your view model, create new file in a folder named SampleData and name it MainViewModelSampleData.xaml.
Now, in the MainPage.xaml add the following attribute to the page element:
d:DataContext={d:DesignData Source=SampleData/MainViewModelSampleData.xaml}
Also set Build Action for MainViewModelSampleData.xaml to DesignData.
Now, if you want to display data in your MainPage, you need to define all properties in the sample data file. For example:
// view model contains public properties Title of type string and Children of type
// PersonViewModel which contains properties Name and Age (string and int respectively)
<local:MainViewModel xmlns:local="clr-namespace:myapp"
Title="Title">
<local:MainViewModel.Children>
<local:ChildViewModel Name="John" Age="31" />
</local:MainViewModel.Children>
</local:MainViewModel>
You should now see your page filled with data in your design view. This way by using MVVM you can create mock data quickly. That will ensure that you can design your view around existing data without running the application.
Read more on the following links:
31 Days of Mango | Day #18: Using Sample Data
Modify sample data
Generate sample data
Using Blend Sample data at Design time and real data at Runtime
I now know how to do that.
If anyone if you guys ever stumble upon this problem, do this:
Copy all the XAML you wrote in the stackpanel of your itemtemplate
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
//...
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
And copy it up to <ListBox> //Here </ListBox>
There you can edit it in the designer.
And when you're done, just copy the code back to the StackPanel.
Related
I am trying to get a journal type gui, where entries are ordered by their date.
I want to fetch my journal entries from the datbase and group the ones that were added on the same day and separete these groups with a label.
Kind of like the facebook messenger chat history.
This is what I was thinking about:
<ScrollView>
<CollectionView ItemsSource="{Binding JournalEntries}" SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label>Date</Label>
<CollectionView ItemsSource="{Binding}">
</CollectionView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ScrollView>
The problem is that 'JournalEntry' object can be of many types.
For example 'Photo' derives from 'JournalEntry'. So if a 'Photo' object is in the 'JournalEntries' collection I want the image to be displayed and not the 'ToString()' text.
What would be he best way to group the JournalEntries into different days, so I can display an label over the group?
How can I decide in runtime wether an object should be displayed as an image or as text?
Any help would be appreciated.
Here are answers:-
You can use JournalEntries.GroupBy(x=> x.date).ToDictionary(). For this you will have to use System.Ling. This will return you a Dictionary, whose Key will be the Date and all the entries in a form of List in the Value. This will be easier for you to use.
You can use a DataTemplateSelector or Converter for choosing an option at runtime. With a DataTemplateSelector you will have to make two DataTemplates one for each Image and Text. For Converter you will have to toggle the visibility on the basis of the data which comes from the Server/Database.
Let me know for further queries. I can help you out for sure!
After some research and some testing I have found a suitable solution for me. Nikhil's answer is correct, but I would say incomplete, specially as it pertains to the DataTemplateSelector part.
First I created a class called EntryGroup which derives from List<Entry>.
After this I created a Property for binding in my ViewModel called Entries which was an IEnumerable of type EntryGroup.
This process is outlined here.
I created a GroupHeaderTemplate to display the dates.
In my ViewModel I get the entries from my repository and use the .GroupBy(it => it.Timestamp.Date) Linq method. This returns an IGrouping<DateTime, Entry>.
Since it already extends IEnumerable, half of the job is done.
I wrote them to the Entries IEnumerable using a foreach loop.
Then I create a class deriving from DataTemplateSelector and created an ImageTemplate and a TextTemplate as outlined here
I need to use a different panel for a particular section/group in my ListView. How do I do that (using XAML, C#, or anything)? I already tried using GroupedStyleSelector but it didn't work (I researched about it but it turned out it's not designed for this purpose). Here's my XAML right now:
<ListView ItemsSource="{Binding Source={StaticResource cvs}}">
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.Panel>
<ItemsPanelTemplate>
// I want to change this for a particular group
<uwp:SGStaggeredPanel/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
I'm thinking of subclassing the panel, but the problem is how do I get a reference to the current group?
https://learn.microsoft.com/en-us/windows/communitytoolkit/extensions/listviewbase
The above article talks about a WCT goody that allows you to dynamically change the Tamplate of the item that is about to be rendered, this particular example is a statically expressed extension that simply works as an attached property to a listview and cycles through two different templates
But you can easily extend ListView into a templated control and then more easily have access to the Viewmodel that houses your Itemsource, from then you can go on to change the
private static void ItemTemplateContainerContentChanging(Windows.UI.Xaml.Controls.ListViewBase sender, ContainerContentChangingEventArgs args)
which is where all the magic takes place.
Notation for implementation
Note 0:
if you don't know mvvm and binding, forget you ever read this and go study it up instead.
Note 1:
All child controls that have no explicitly defined Data Context will inherit their parents.
Note 2:
You will be able to Map incoming controls in the aforementioned function by tracking the incoming args.ItemIndex and then cross checking it with the binded source (Observable list etc) that is housed on the underlying datacontext.
Note 3:
To convert this into a tamplated/custom control you will have to pretty much make your own implementation of ListView like this MyListview:ListView
The Dependency properties will have to be converted to conventional ones,
just type 'propdp' and double tap Tab, to bring up the default tamplate.
You will still have to reference all the different DataTamplates from XAML like its shown in the showcase app listed bellow.
Note 4:
Cut the slack off that showcase code, the stretch direction and the zebra stripes for example are not needed in your case.
https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.cs
this is the exact location of the code piece i talked about, to check it out in action and play with it, Download 'Windows Community Toolkit' from the store, it is in the Extensions section.
I'm building a UWP app using the Template10 Minimal template. I have a list of ViewModels that share a common base class. They are bound to a Pivot as follows:
ItemsSource="{Binding EnabledModels}"
I've setup multiple data templates to map each ViewModel concrete type to the View (UserControl) created for that particular ViewModel as follows:
<Pivot.Resources>
<DataTemplate x:Key="gettingStarted" x:DataType="vm:GettingStartedViewModel">
<v:GettingStartedPart DataContext="{Binding}"></v:GettingStartedPart>
</DataTemplate>
<DataTemplate x:Key="packageSelection" x:DataType="vm:PackageSelectionViewModel">
<v:PackageSelectionPart DataContext="{Binding}"></v:PackageSelectionPart>
</DataTemplate>
</Pivot.Resources>
I've not been able to determine how to get the View to actually display. Currently it will only display the type name of the ViewModel. I'm sure I've messed up the bindings somehow.
My ultimate goal is to present a Pivot with a series of data collection screens that all share a common base ViewModel, but each screen has it's own data needs. I'd like to keep the screens as separate UserControl views and dedicated ViewModels to make them easier to maintain independently.
I've looked for other patterns for multi-screen data capture in UWP that don't require separate pages but haven't had any luck.
Thanks for any guidance you can provide!
I was able to get the DataTemplateSelector to work so the Pivot would display a PivotItem containing the appropriate View (UserControl) for each ViewModel in my list of EnabledModels. Thank you to commenters Will and AVK Naidu.
Good resource for this situation available here
I'm just now entering the realm of WPF, and I would like to create a custom control. I'm not too concerned about the styling of it, but rather the functionality.
I'm trying to create a custom Tree View that auto-magically fills up with data from a different library so that other WPF applications are able to use this control, and expose this data to their users, and get feedback with ease.
IE:
+-----------------------------+
|+Project |
|+-- File |
|+---- Patch |
|+Other Project |
|+-- Files Are Nifty |
|+---- Yup. |
+-----------------------------+
I want ^that^ to be a re-useable control that should always have the same data among all of its instances. Essentially, a default data-binding. I've done a bit of googling, and I searched in here but all of the questions / answers either weren't relevant, were over my head, or both. The only bit of useful information I found was that in the data-provider to have two branches, return a CompositeCollection.
If somebody could explain this, step-by-step for a WPF Custom Control library, I would much appreciate it.
If your data is always in the same pattern (e.g. Projects have Files, which have Patches), you dont necessarily need a CompositeCollection.
You could also use HierarchicalDataTemplates to display your data like so:
<HierarchicalDataTemplate x:Key="ProjectTemplate"
ItemsSource="{Binding ChildCollection}"
ItemTemplate="{StaticResource FileTemplate}">
<here comes the actual TreeViewItem of this Template>
</HierarchicalDataTemplate>
Your FileTemplate is another HierarchicalDataTemplate, this goes on until you reached the lowest level, this will be a DataTemplate.
All these templates are placed in the resources, now all you have to to is setting the TreeView's ItemTemplate to the top level HierarchicalDataTemplate (ProjectTemplate):
<TreeView ItemsSource="{Binding Path=ChildCollection, UpdateSourceTrigger=PropertyChanged}" ItemTemplate="{StaticResource ResourceKey=ProjectTemplate}">
</TreeView>
Your Data could look like the following:
public class Project
{
public ObservableCollection<File> ChildCollection {get;set;}
}
A File has another ChildCollection (e.g. ObservableCollection<Patch>) a.s.o.
To set a default DataContext, you could set the DataContext in the constructor to a ViewModel which has a ChildCollection of Type ObservableCollection<Project>.
I have a basic project in WPF.
All it does it retrieve / update products.
As shown in the image below, the user enters an ID, the data is then displayed according to it, and the user is able to change the data and click 'Save Product' to save it to the database.
The GetProduct(int id) function retrieves a product by the ID provided.
The SaveProduct() function saves the changed fields.
Also, there are two DataTemplates:
1) For the ProductModel - includes 3 textboxes: ProductId, ProductName, UnitPrice.
2) For the ProductViewModel - includes the save/get buttons + a textbox for the user to enter the id of the desired product.
What I'm trying to do is get the changed data when a user clicks the 'Save Product' button.
The most ideal way in my opinion, is to use Binding.
Each textbox is already binded, but I have no idea how to get the binded data.
Here is an example of a binded textbox in the FIRST DataType (ProductModel):
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding ProductId}" Margin="5" Width="150" />
There is one for each of the following properties: ProductId, ProductName and UnitPrice.
IMPORTANT!: The Get/SaveProduct() functions are in the ProductViewModel class, while the actual product class is - you guessed it - ProductModel. The ProductViewModel class holds a variable that contains the current product displayed.
This is the button that's used to save the info - it is written in the SECOND DataType (ProductViewModel):
<Button Content="Save Product" DockPanel.Dock="Right" Margin="10,2" VerticalAlignment="Center" Command="{Binding Path=SaveProductCommand}" Width="100" />
The SaveProductCommand command simply fires the SaveProduct() function.
I have a few questions regarding this whole subject:
What does it mean when a binding is used like this : {Binding ProductId} ?
The default binding mode for textboxes is TwoWay as far as I remember. But in this case, ProductId/Name + UnitPrice are not dependency properties, therefore is it right that the binded values do not update/sent back when the text in the textboxes is changed? (Since there isn't an event attached to it...)
A data context was never configured in my project, but all of the "binding tags" in my XAML pages don't seem to have a defined source. Could it be that the source is actually the DataType in the DataTemplate that includes the binded objects?
The SECOND DataTemplate (the ProductViewModel one) has this ContentControl tag: <ContentControl Margin="10" Content="{Binding Path=CurrentProduct}" />.
What is it's purpose?
If a TwoWay binding were/does occur, how do I get the values from within the SaveProduct() function? Do I just refer to, say CurrentProduct.ProductName to get the changed name?
Much thanks to everyone who takes their time to answer - I appreciate it so much!
What does it mean when a binding is used like this : {Binding
ProductId} ?
The specific control property you have this binding set on is going to look for the ProductId property on the object set as the DataContext and set the propertys value in the control accordingly.
The default binding mode for textboxes is TwoWay as far as I remember.
But in this case, ProductId/Name + UnitPrice are not dependency
properties, therefore is it right that the binded values do not
update/sent back when the text in the textboxes is changed? (Since
there isn't an event attached to it...)
You do not need to make the properties within your object a DependencyProperty for TwoWay binding to occur.
A data context was never configured in my project, but all of the
"binding tags" in my XAML pages don't seem to have a defined source.
Could it be that the source is actually the DataType in the
DataTemplate that includes the binded objects?
The bindings being set within your XAML will use the object stored within the DataContext, thus if you do not explicitly set the DataContext of the view, it will be null. You should note however that the DataContext is inherited from its parent. If you are in fact setting the content by using say, CurrentProduct, then all the properties will be available to bind to per your Product type.
The SECOND DataTemplate (the ProductViewModel one) has this
ContentControl tag:
<ContentControl Margin="10" Content="{Binding Path=CurrentProduct}" />
What is it's purpose?
It is acting as the container of your CurrentProduct, which can contain one and only one item.
If a TwoWay binding were/does occur, how do I get the values from
within the SaveProduct() function? Do I just refer to, say
CurrentProduct.ProductName to get the changed name?
Without seeing the entire application, my guess is that the ContentControl is being set to the CurrentProduct and your TextBox, etc.. are all bound to the respective properties, such as CurrentProduct.ProductId, etc... The product which you want to save is in fact the CurrentProduct. When you call save within your ViewModel, you simply access the CurrentProduct and persist it as needed, where CurrentProduct.PropertyName will contain the changes which were propagated from the UI.