WPF Custom Control Default Databinding - c#

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>.

Related

How to use different panels in a grouped list view?

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.

WPF and MVVM: bind UserControl to XAML dynamically

seems like a trivial task: i am building a wpf application, using MVVM pattern. what i want is dynamically change part of a view, using different UserControls, dependent on user input.
let's say, i have got 2 UserControls, one with a button, and another with a label.
in main view i have a container for that. following XAML "works":
<GroupBox Header="container" >
<local:UserControlButton />
</GroupBox>
and a UserControl element with buttons pops up. if i change it to another one, it works too.
question is how to feed that groupbox dynamically. if i put something like that in my model view:
private UserControl _myControl;
public UserControl MyControl
{
get
{
return _myControl;
}
set
{
_myControl= value;
InvokePropertyChanged("MyControl");
}
}
and change my view XAML to something like:
<GroupBox Header="container" >
<ItemsControl ItemsSource="{Binding MyControl}" />
</GroupBox>
and feed it from command with usercontrol for button or for label: nothing happens, although "MyControl" variable is set and is "invoke property changed"..
Obviously there are many ways to skin this particular cat - but to answer the question of why it doesn't work you need to look into the ItemsSource property of ItemsControl on MSDN.
The items control is designed to show multiple items, provided through an IEnumerable passed to the ItemsSource property. You are passing a UserControl, so the binding will fail.
For your example, I would change the ItemsControl to a ContentControl and bind the content to your MyControl property. This should then work.
<GroupBox Header="container" >
<ContentControl Content="{Binding MyControl}" />
</GroupBox>
However, I would strongly recommend looking into other ways of doing this - having a control in your VM breaks MVVM to my mind. Depending on what you are doing look at data templates - #Sheridan's link in the comments provides an great description of a way to do it.
Couldn't post this as a comment so adding as answer..
Have a look at this:
Implementing an own "Factory" for reusing Views in WPF
It uses DataTemplates but doesn't require the DataTemplate section for each view. If you potentially have a lot of user controls/views you wish to display or you are reusing through multiple views or you are intending to actually dynamically generate a view (versus just loading an existing user control) then this might suite your needs.

MVVM and dynamic generation of controls

i've written a tool that generates sql queries using GUI, i want to rewrite the tool using MVVM and WPF, every sql column type has a different control as you can see in the following image
i add a column filter control based on the sql column type, and i generate the controls using code, just like i used to do in windows forms.
in MVVM i've read that the view is writtien enteirly using XAML,
does MVVM suite such application where i have to add different user
controls dynamically to a stack panel?
The controls won't exist in the view unless some column is double clicked, that means the control won't be available in the xaml and won't be hidden or collapsed.
is there any way that i can avoid the bindings in the code behind?
should i create a user control for each column type?
in general what is the best approach to devlop such application with complex and dynamic ui using mvvm?
Guess I know how to achieve that, but it is very complex stuff. First you should comprehend MVVM basic concepts.
Main ViewModel should be a class with ObservableCollection of ViewModels, each of them represents a column with its data and properties.
interface IViewModel : INotifyPropertyChanged,IDisposable
{
}
interface IColumnViewModel : IViewModel
{
}
class ViewModelBase : IViewModel
{
// ... MVVM basics, PropertyChanged etc. ...
}
class MainViewModel : ViewModelBase
{
ObservableCollection<IColumnViewModel> Columns {get; set}
}
In View I suppose something like ItemsControl with ItemTemplate, that should embed ContentControl with DataTemplate, that shall be automatically selected by WPF according to binded DataContext of list item. StackPanel itself is not suitable for that, but it can be invoked as ItemsPanelTemplate
<Window
xmlns:v="clr-namespace:WpfApplication.Views"
xmlns:vm="clr-namespace:WpfApplication.ViewModels">
<Window.Resources>
<DataTemplate DataType="{x:Type TypeName=vm:TextColumnViewModel}">
<v:TextColumnView/>
</DataTemplate>
</Window.Resources>
<ItemsControl
ItemsSource="{Binding Columns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
So, you should build View/ViewModel pair for every column type.
Hope, my example will help. Good luck with your girlfriend and MVVM :)
If I've understood your scenario correctly :
You can use Data Templates & Items Templates
For example I've written an application which loads Data into Collection and then shows each item of that collection in a Wrap Panel [ Or stack panel ] based on defined data template.
And Wrap penel items are in sync by the collection itself within two way binding
You should consider using Observable Collections to achieve this goal
Then you can fill the collection and see the results on a view
I hope this helps
To write something like this in MVVM, you would have one view that is say, your content area. That view would have a view model, one of the properties of that view model would be a view, or several properties of that view model would be a view. It takes a bit to wrap your head around at times, but if you use Inversion of Control and Dependency Injection properly a view of views is very manageable in an MVVM pattern.
Well, your view isn't written entirely in XAML - you generate controls in C#.
I don't think you'll gain something from rewriting this and fitting it into an MVVM mold. Just keep the code as it is now and enjoy.

Editing ListBox with Live/Preview?

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.

Overriding default template for specific nodes in a databound (XML/XPath) TreeView

TL;DR up front: I would like to use a "default" HierarchicalDataTemplate for all but a specific few nodes in a WPF TreeView. These nodes come from an XMLDocument and are not fully known until runtime. Is there a way to do this?
At runtime, I am analyzing specific parts of a system, and building an XML document that I'm then binding to a TreeView like so:
MyTree.DataContext = MyXMLDocument;
This is my WPF declaration of the TreeView:
<TreeView x:Name="MyTree" ItemsSource="{Binding Mode=OneWay, XPath=/Analysis}" ItemTemplate="{StaticResource GenericElementWithChildren}"/>
The template starts like this...
<HierarchicalDataTemplate x:Key="GenericElementWithChildren" ItemsSource="{Binding XPath=child::node()}">
So for example, I might have some long XML document about different aspects of the analysis I just ran and I'd like some particular element like "Disk" or "Proprietary Foo Service" to have a special template because it should be displayed nicely, while everything else just gets a generic template.
I've thought about using a DataTemplateSelector but it seems like there must be a better way. I'm not even sure how I'd do that for XML. Do any of you smarter folks have any wisdom to impart, or am I stuck figuring out how to write a DataTemplateSelector against XML?
DataTemplate and HierarchicalDataTemplate also have a DataType property.
When you remove the x:Key and supply a DataType, these Templates are implicit. So you can define your different templates as implicit and they will be used automatically, as long as you don't supply a ItemTemplate or ItemTemplateSelector on the inital TreeView.
<HierachicalDataTemplate DataType="{x:Type ProprietaryFooService}">
Personally i try to avoid that, because this also means that other controls that can show your data are using these Templates aswell. Imo the best would be to use a DataTemplateSelector, especially when you deal with the same types that you need to show in different ways.
Edit:
Sorry i missed that you are using Xpath. I guess it will not work there. I will leave this answer here, but can't guarantee that it suits your needs. Maybe it helps in another way anyway.

Categories