ViewModel Views relation/link/synchronization - c#

Third try to describing problem:
Try 1:
Sunchronizing view model and view
Try2:
WPF ViewModel not active presenter
Try3:
I have some class for view models:
public class Node : INotifyPropertyChanged
{
Guid NodeId { get; set; }
public string Name { get; set; }
}
public class Connection: INotifyPropertyChanged
{
public Node StartNode { get; set; }
public Node EndNode { get; set; }
}
public class SettingsPackModel
{
public List<Node> Nodes { get; private set; }
public List<Connection> Connections { get; private set; }
}
I also have some templates to displays these models:
<DataTemplate DataType="{x:Type vm:Node}">…</DataTemplate>
<DataTemplate DataType="{x:Type vm:Connection}">
<my:ConnectionElment StartNodeElment="???" EndNodeElment="???">
</my:ConnectionElment>
<DataTemplate>
But the problem is that DataTemplate for Connection need reference ot two element of type UIElement , how can I pass these two, how can I fill ??? in above expression?
Edit:
I actually want to hide that's part in this try, but as I describe it there: Sunchronizing view model and view. I would use something like this :
<ItemsControl ItemsSource="{Binding AllElements}"
ItemContainerStyle="{StaticResource ElementThumbVMDataTemplateStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<kw:DiagramCanvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Style x:Key="ElementThumbVMDataTemplateStyle" TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding CanvasLeft,Mode=TwoWay}" />
<Setter Property="Canvas.Top" Value="{Binding CanvasTop,Mode=TwoWay}" /> </Style >
And something like this for Node DataTemplate:
<DataTemplate DataType="{x:Type vm:Node}">
<kw:ElementThumb Canvas.Left="{Binding CanvasLeft,Mode=TwoWay}"
Canvas.Top="{Binding CanvasTop,Mode=TwoWay}">
</kw:ElementThumb>
</DataTemplate>
Canvasleft and CanvasTop are properties that exist in Node and also ElementThumb classes.

Placing an object of the type you are creating a DataTemplate for inside the DataTemplate itself is quite pointless. DataTemplates are there for the creation of a visual representation of your data, so you first need a concept of how you want to visualize your Nodes and Connections.

Related

How do I bind an object that is periodically modified to a treeview in C# WPF using the CommunityToolkit.MVVM?

I have a treeView defined in XAML as:
<UserControl.Resources>
<models:TreeLines x:Key="myLines" x:Name="myLinesData"/>
</UserControl.Resources>
<TreeView
x:Name="treeData"
Grid.Column="1"
Padding="0,5,0,0"
Background="#282828"
BorderThickness="0"
SelectedValuePath="Uid">
<TreeViewItem
x:Name="tLines"
Uid="tabLines"
Header="Lines"
ItemsSource="{Binding Source={StaticResource myLines}, Path=MyLines}"
Style="{StaticResource custTVItem}">
<TreeViewItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Lines}" ItemsSource="{Binding lineSet}">
<TextBlock Text="{Binding productName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:LineSets}" ItemsSource="{Binding lineName}">
<TextBlock Text="{Binding setName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:LineNames}" ItemsSource="{Binding dataTypes}">
<TextBlock Text="{Binding lineName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:LineData}" ItemsSource="{Binding dataVals}">
<TextBlock Text="{Binding dataType}" />
</HierarchicalDataTemplate>
</TreeViewItem.Resources>
</TreeViewItem>
</TreeView>
The UserControl.Resources is pointing towards a class:
public partial class TreeLines : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(MainWindow.treeData.ItemsSource))]
private List<Lines>? myLines;
}
The error I get here is:
The target(s) of [NotifyPropertyChangedFor] must be a (different) accessible property
The object myLines I'm trying to bind to has the classes behind, as seen in the TreeView `HierarchicalDataTemplates:
public class Lines
{
public string productName { get; set; }
public List<LineSets> lineSet { get; set; }
}
public class LineSets
{
public string setName { get; set; }
public List<LineNames> lineName { get; set; }
}
public class LineNames
{
public string lineName { get; set; }
public List<LineData> dataTypes { get; set; }
}
public class LineData
{
public string dataType { get; set; }
public List<double> dataVals { get; set; }
}
If I remove all the CommunityToolkit.MVVM aspects and set my variable:
private List<Lines>? myLines; manually by changing it to public and assigning data to it on loading, then it populates on load only.
I need to modify myLines on the fly within my C# code which in-turn should update the treeView. You can see I'm trying to achieve this automatically with the data binding but something isn't right.
I think the mistakes could possibly be in the line:
[NotifyPropertyChangedFor(nameof(MainWindow.treeData.ItemsSource))]
and/or possibly the StaticResource usage in XAML:
<TreeViewItem
x:Name="tLines"
Uid="tabLines"
Header="Lines"
ItemsSource="{Binding Source={StaticResource myLines}, Path=linesCollection}"
Style="{StaticResource custTVItem}">
Please advise if you can help
Replace all List<T> properties with ObservableCollection<T>. Then the view will be updated whenever you add or remove items from these collections.
For the view to also update when you change a property of an individual item in a collection, the class of the property that you change should implement the INotifyPropertyChanged interface and raise change notifications.
Here is an example of how you should implement the Lines class:
public class Lines : ObservableObject
{
[ObservableProperty]
private string productName { get; set; }
[ObservableProperty]
private ObservableCollection<LineSets> lineSet { get; set; }
}
Bind to the generated properties (starting with an uppercase letter):
<HierarchicalDataTemplate DataType="{x:Type models:Lines}"
ItemsSource="{Binding LineSet}">
<TextBlock Text="{Binding ProductName}"/>
</HierarchicalDataTemplate>
[NotifyPropertyChangedFor(nameof(MainWindow.treeData.ItemsSource))] does not need to be added.
There is no need to implement additional notifications. Because [ObservableProperty] is already implementing the notification function.
Check out the auto-generated sources.
[NotifyPropertyChangedFor(parameter)]'s parameter should be the name of property inside the class.
public partial class TreeLines : ObservableObject
{
[ObservableProperty]
private List<Lines>? myLines;
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
}
In this case, the possible Arguments of [NotifyPropertyChangedFor] are only MyLines, OtherProperty1 , and OtherProperty2.
[NotifyPropertyChangedFor] is an attribute indicating that other properties connected within the class have changed
Here's an example.
public partial class GetSum : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Sum))]
private int num1;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Sum))]
private int num2;
public int Sum { get => num1 + num2; }
}
When calling the setter of Num1 Property,
simultaneously update the Num1 value and Sum value bound to the screen.
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp2"
Height="450" Width="800">
<Window.DataContext>
<local:GetSum/>
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Num1}"/>
<TextBox Text="{Binding Num2}"/>
<TextBlock Text="{Binding Sum}"/>
</StackPanel>
</Window>

Build a TreeView based on the object obtained by reflection

It is necessary to display the element, presented in the form as in the picture, in the form of a tree. The object itself is created at compile time.
An attempt to make in this form
<TreeView
Grid.Row="1"
Grid.Column="0"
ItemsSource="{Binding LanguageInformation.Items}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:LocalizationBrowserViewModel}" ItemsSource="{Binding LanguageInformation.Items}">
<TextBlock Text="{Binding}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Class for presentation
public class Element
{
public string Name { get; set; }
public ObservableCollection<object> Items { get; set; }
public Element()
{
this.Items = new ObservableCollection<object>();
}
}
The Element class now allows only one level of hierarchy. To support N levels, the Element class needs to be changed as below
public class Element
{
public string Name { get; set; }
public ObservableCollection<Element> Items { get; set; }
public Element()
{
this.Items = new ObservableCollection<Element>();
}
}
TreeView needs to be replaced with a ListView because the screenshot provided in the question contains multiple columns. TreeView does not allow multiple columns but a ListView.
ListView in WPF supports HierarchicalDataTemplate. You can refer HierarchicalDataTemplate in WPF for the implementation.

WPF Fill TreeView from Database

I have one problem using TreeView in WPF using the MVVM design pattern.I found a solution that helped me a lot for understanding the principals, but when I applied it in my project, I can't get the desired results. The article is this one.
My base class is:
public class ClientTreeViewProducts
{
public List<ClientTreeViewProducts> Items { get; set; }
public string Name { get; set; }
}
In the ViewModel I have a public property that I bind to the ItemsSource property of the TreeView:
private List<ClientTreeViewProducts> _treeViewSource;
public List<ClientTreeViewProducts> TreeViewSource
{
get { return _treeViewSource; }
set
{
if(_treeViewSource!=value)
{
_treeViewSource = value;
RaisePropertyChanged("TreeViewSource");
}
}
}
I have a function that fills the List, and it seems to be OK.
And finally here is my xaml that manages the bindings and the HierarchicalDataTemplate :
<TreeView ItemsSource="{Binding TreeViewSource}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type helpers:ClientTreeViewProducts}"
ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The problem is when I fill some data in the TreeViewSource property, the tree view remains empty (visually). Can someone find what the problem is, because I'm head-banging about this problem for 3 days.
Thank you in advance!

Sorting ItemsControl items based on a property defined declaratively in DataTemplate XAML

I'm looking for a simple way to sort items of ItemsControl based on a property specified in implicit DataTemplate for the items to which the control is bound. And defining the properties on DataTemplate is crucial here, because I cannot add the sorting property on the item itself.
So, for the below example VM layer:
public interface INamed
{
string Name { get; set; }
}
public class FirstModel : INamed
{
public string Name { get; set; }
}
public class SecondModel : INamed
{
public string Name { get; set; }
}
public class ViewModel
{
public ViewModel()
{
Models = new INamed[] { new SecondModel {Name = "Second"}, new FirstModel {Name = "First"}};
}
public IEnumerable<INamed> Models { get; private set; }
}
and this attached property:
public static class AttachedProperties
{
public static int GetSortOrder(DependencyObject obj)
{
return (int)obj.GetValue(SortOrderProperty);
}
public static void SetSortOrder(DependencyObject obj, int value)
{
obj.SetValue(SortOrderProperty, value);
}
public static readonly DependencyProperty SortOrderProperty =
DependencyProperty.RegisterAttached("SortOrder", typeof(int), typeof(AttachedProperties), new PropertyMetadata(0));
}
I have the following DataTemplate definitions (over-simplified):
<DataTemplate DataType="{x:Type local:FirstModel}">
<StackPanel Background="Red" local:AttachedProperties.SortOrder="1">
<Label>First's Name:</Label>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SecondModel}">
<StackPanel Background="Green" local:AttachedProperties.SortOrder="2">
<Label>Second's Name:</Label>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
Somewhere the usage will be like:
<ItemsControl ItemsSource="{Binding Models}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
And here the order of the items should be based on the attached property I defined for the data templates. Don't see any option to use the CollectionViewSource directly here, may be I'm wrong...
Current options I see, none too appealing, are:
Attached behavior on the ItemsControl, traversing the visual tree of each new item and sorting the Items in accordance with the found SortOrder value
A custom ItemsControl with it's own sorting logic, panel, blackjack and... you know
Wrapping the model instances in some kind of proxy with SortOrder property on it. Which still requires some custom/user control code-behind or ViewModel class changes
Is there some better/easier way I miss?
I guess you cant
I think the only way is to implement your own ItemsControl
Or wrap the models with another class
Maybe this helps:
SortDescription with custom attached property

Binding list of objects to ItemsControl with custom ItemTemplate in MVVM

Current Setup
I have a custom class representing an installer file and some properties about that file, conforming to the following interface
public interface IInstallerObject
{
string FileName { get; set; }
string FileExtension { get; set; }
string Path { get; set; }
int Build { get; set; }
ProductType ProductType { get; set; }
Architecture ArchType { get; set; }
bool Configurable { get; set; }
int AverageInstallTime { get; set; }
bool IsSelected { get; set; }
}
My ViewModel has a ReadOnlyObservableCollection<IInstallerObject> property named AvailableInstallerObjects.
My View has a GroupBox containing the ItemsControl which binds to the aforementioned property.
<GroupBox Header="Products">
<ItemsControl ItemsSource="{Binding Path=AvailableInstallerObjects}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"
VerticalAlignment="Center" Margin="5"/>
<TextBlock Text="{Binding Path=FileName}" Margin="5" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
The binding works correctly, except it's not user friendly. 100+ items are shown.
Need Help Here
I'd like to be able to use my collection of IInstallerObjects but have the View present them with the following ItemTemplate structure.
<GroupBox Header="Products">
<ItemsControl ItemsSource="{Binding Path=AvailableInstallerObjects}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"
VerticalAlignment="Center" Margin="5"/>
<TextBlock Text="{Binding Path=ProductType}" Margin="5" />
<ComboBox ItemsSource="{Binding Path=Build}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
Basically I want to be able to group by the ProductType property, showing a list of the available products, with the ComboBox representing the available Build property values for IInstallerObjects of the ProductType.
I can use LINQ in the ViewModel to extract the groupings, but I have no idea how I'd bind to what I've extracted.
My research also turned up the possibility of using a CollectionViewSource but I'm not certain on how I can apply that to my current setup.
I appreciate your help in advance. I'm willing to learn so if I've overlooked something obvious please direct me to the information and I'll gladly educate myself.
If Build should be a collection type.
so your class should be structured like this as an example.
Public Class Customer
Public Property FirstName as string
Public Property LastName as string
Public Property CustomerOrders as observableCollection(OF Orders)
End Class
This should give you the expected results. Each item in the main items presenter will show first name last name and combobox bound to that customers orders.
I know it's simple but this should do.
All you have to do is declare a CollectionViewSource in your view and bind it to the ObservableCollection. Within this object you declare one or more GroupDescriptions which will split up the source into several groups.
Bind this source to the listbox, create a Template for the group description and you are done.
An example can be found here: WPF Sample Series – ListBox Grouping, Sorting, Subtotals and Collapsible Regions. More about CollectionViewSource can be found here: WPF’s CollectionViewSource
The description of your problem lead me to believe you are looking for some kind of colapsing / expanding / grouped / tree-view sort of thing.
XAML for the tree-view
<Window x:Class="WPFLab12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:WPFLab12"
Title="MainWindow" Height="350" Width="525">
<Grid>
<GroupBox Header="Products">
<TreeView ItemsSource="{Binding Path=ProductTypes}">
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type loc:ProductType}"
ItemsSource="{Binding AvailableInstallerObjects}">
<TextBlock Text="{Binding Description}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:InstallerObject}">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"
VerticalAlignment="Center" Margin="5"/>
<TextBlock Text="{Binding Path=FileName}" Margin="5" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</GroupBox>
</Grid>
</Window>
What does that do? Well, it establishes a hierarchy of controls in the tree based on the type of data found. The first HierarchicalDataTemplate handles how to display the data for each class, and how they are related in the hierarchy. The second HierarchicalDataTemplate handles how to display each InstallerObject.
Code behind for the Main Window:
public partial class MainWindow : Window
{
public ReadOnlyObservableCollection<ProductType> ProductTypes
{
get { return (ReadOnlyObservableCollection<ProductType>)GetValue(ProductTypesProperty); }
set { SetValue(ProductTypesProperty, value); }
}
// Using a DependencyProperty as the backing store for ProductTypes. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ProductTypesProperty =
DependencyProperty.Register("ProductTypes", typeof(ReadOnlyObservableCollection<ProductType>), typeof(MainWindow), new UIPropertyMetadata(null));
public MainWindow()
{
this.InitializeComponent();
this.ProductTypes = new ReadOnlyObservableCollection<ProductType>(
new ObservableCollection<ProductType>()
{
new ProductType()
{
Description = "Type A",
AvailableInstallerObjects = new ReadOnlyObservableCollection<InstallerObject>(
new ObservableCollection<InstallerObject>()
{
new InstallerObject() { FileName = "A" },
new InstallerObject() { FileName = "B" },
new InstallerObject() { FileName = "C" },
})
},
new ProductType()
{
Description = "Type B",
AvailableInstallerObjects = new ReadOnlyObservableCollection<InstallerObject>(
new ObservableCollection<InstallerObject>()
{
new InstallerObject() { FileName = "A" },
new InstallerObject() { FileName = "D" },
})
}
});
this.DataContext = this;
}
}
This is totally cheating, though - normally the MainWindow.cs would not serve as the DataContext and have all this stuff. But for this example I just had it make a list of ProductTypes and populate each ProductType class with the InstallerObject instances.
Classes I used, note I made some assumptions and modified your class to suit this View Model better:
public class InstallerObject
{
public string FileName { get; set; }
public string FileExtension { get; set; }
public string Path { get; set; }
public int Build { get; set; }
public bool Configurable { get; set; }
public int AverageInstallTime { get; set; }
public bool IsSelected { get; set; }
}
public class ProductType
{
public string Description { get; set; }
public ReadOnlyObservableCollection<InstallerObject> AvailableInstallerObjects
{
get;
set;
}
public override string ToString()
{
return this.Description;
}
}
So, in MVVM, it seems to me that your current InstallerObject class is more of a Model layer sort of thing. You might consider transforming it in your ViewModel to a set of collection classes that are easier to manage in your View. The idea in the ViewModel is to model things similarly to how they are going to be viewed and interracted with. Transform your flat list of InstallerObjects to a new collection of hierarchical data for easier binding to the View.
More info on various ways to use and customize your TreeView: http://www.codeproject.com/Articles/124644/Basic-Understanding-of-Tree-View-in-WPF

Categories

Resources