WPF treeview with databinding using multiple data sources - wpf

I have a tree view which is used by 2 different sources (Mission, Target) depending on the mode the GUI is in. Only 1 source is displayed at any one time
Class information:
Mission Class
public class Mission
{
public string Name { get; set; }
public List<Target> PotentialTargets { get; set; }
public List<Target> SelectedTargets { get; set; }
public List<Operation> OpList { get; set; }
//other properties
}
Operation Class
public class Operation
{
public string Label { get; set; }
//other properties
}
Target
public class Target
{
public string Label { get; set; }
public TargetType Type { get; set; }
public int Priority { get; set; }
//other properties
}
TargetType
public enum TargetType
{
Star,
Line
}
I am trying to achieve the following display on the TreeView when setting the itemSource to a different source.
Mission Source
- Mission_1(Name)
- Potential_Target
- Target_1(Label)
- Target_2(Label)
- Selected_Target
- Target_1(Label)
- Operation
- Op_1(Name)
- Mission_2(Name)
- Potential_Target
- Target_1(Label)
- Target_4(Label)
- Selected_Target
- Target_4(Label)
- Operation
- Op_1(Name)
Target Source
- Priority_1(Priority)
- Star(TargetType)
- Star_Target_1(Label)
- Line(Type)
- Line_Target_1(Label)
- Priority_2(Priority)
- Star(TargetType)
- Star_Target_2(Label)
- Star_Target_3(Label)
All help will be appreciated.
Thanks in advance.

You need some base class to represent your tree nodes in TreeView.
For example you can declare you classes like this:
ABaseNode
// Base class to provide access to nodes Children
public abstract class ABaseNode
{
private ObservableCollection<ABaseNode> children;
public ObservableCollection<ABaseNode> Children
{
get { return children ?? (children = new ObservableCollection<ABaseNode>()); }
set { children = value; }
}
}
LabeledNode
//Simple node with label to display
public class LabeledNode : ABaseNode
{
public LabeledNode(string label)
{
Label = label;
}
public string Label { get; private set; }
}
Mission Class
public class Mission : LabeledNode
{
public Mission(string label) : base(label)
{
PotentialTargetsNode = new LabeledNode("PotentialTargets");
Children.Add(PotentialTargetsNode);
SelectedTargetsNode = new LabeledNode("SelectedTargets");
Children.Add(SelectedTargetsNode);
OpListNode = new LabeledNode("Operation");
Children.Add(OpListNode);
}
public LabeledNode PotentialTargetsNode { get; private set; }
public LabeledNode SelectedTargetsNode { get; private set; }
public LabeledNode OpListNode { get; private set; }
}
Operation Class
public class Operation : LabeledNode
{
public Operation(string label) : base(label)
{
}
//other properties
}
Target Class
public class Target : LabeledNode
{
public Target(string label) : base(label)
{
}
public TargetType Type { get; set; }
public int Priority { get; set; }
//other properties
}
PriorityNode Class
public class PriorityNode : LabeledNode
{
public PriorityNode(int priority) : base("Priority: " + priority)
{
StarTargetsNode = new LabeledNode("Star");
Children.Add(StarTargetsNode);
LineTargetsNode = new LabeledNode("Line");
Children.Add(LineTargetsNode);
}
public LabeledNode StarTargetsNode { get; private set; }
public LabeledNode LineTargetsNode { get; private set; }
}
Xaml of MainWindow contents
<StackPanel>
<Button Content="Mission source" Click="MissionButtonClick"/>
<Button Content="Target source" Click="TargetsButtonClick"/>
<TreeView x:Name="treeView" Height="250" ItemsSource="{Binding Missions}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:LabeledNode}" ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=Label}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:LabeledNode}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Label}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</StackPanel>
Data initialization
public partial class MainWindow : Window
{
public ObservableCollection<Mission> Missions { get; private set; }
public ObservableCollection<PriorityNode> TargetsByPriority { get; private set; }
public MainWindow()
{
InitializeComponent();
var mission = new Mission("Mission1");
mission.PotentialTargetsNode.Children.Add(new Target("Target1") {Type = TargetType.Line, Priority = 1});
mission.PotentialTargetsNode.Children.Add(new Target("Target2") {Type = TargetType.Star, Priority = 2});
mission.SelectedTargetsNode.Children.Add(new Target ("SelectedTarget") { Type = TargetType.Line, Priority = 1 });
mission.OpListNode.Children.Add(new Operation("Op1"));
Missions = new ObservableCollection<Mission> { mission };
var priorityNode = new PriorityNode(1);
priorityNode.StarTargetsNode.Children.Add(new Target("Target3") { Type = TargetType.Star, Priority = 1 });
priorityNode.LineTargetsNode.Children.Add(new Target("Target1") { Type = TargetType.Line, Priority = 1 });
priorityNode.LineTargetsNode.Children.Add(new Target("Target2") { Type = TargetType.Line, Priority = 1 });
TargetsByPriority = new ObservableCollection<PriorityNode> { priorityNode};
DataContext = this;
}
private void MissionButtonClick(object sender, RoutedEventArgs e)
{
treeView.ItemsSource = Missions;
}
private void TargetsButtonClick(object sender, RoutedEventArgs e)
{
treeView.ItemsSource = TargetsByPriority;
}
}
Now click the buttons to change your TreeView ItemsSource.

Related

Unable to Bind ListBox to IEnumerable<object>

This is the first time I'm working with List
Here is my code (Removed all the unwanted lines for simplification)
Class1
public class RInfo
{
public int RCode { get; set; }
public int ACode { get; set; }
public int DCode { get; set; }
public string RNo { get; set; }
//public DateTime EDate { get; set; }
//public DateTime? VDate { get; set; }
}
Class 2
public class DInfo
{
public int DCode { get; set; }
public string DName { get; set; }
public bool DCExpires { get; set; }
}
Class that I will get after joining above two classes
public class RInfo_Details
{
public int RCode { get; set; }
public int ACode { get; set; }
public int DCode { get; set; }
public string DCName { get; set; }
public string DName { get; set; }
public string RNo { get; set; }
//public DateTime EDate { get; set; }
//public DateTime? VDate { get; set; }
}
Here is my class this is used to retrive data and for binding in XAML
public class AppData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private AppData() { }
private static AppData instance = null;
public static AppData Instance
{
get
{
if (instance == null) instance = new AppData();
return instance;
}
}
AInfo _SelAInfo;
public AInfo SelAInfo
{
get => _SelAInfo; set
{
_SelAInfo = value; RaisePropertyChanged(nameof(SelAInfo));
RaisePropertyChanged(nameof(RInfo_List));
RaisePropertyChanged(nameof(RInfoDetailedList));
}
}
List<DInfo> _DInfo;
public List<DInfo> DInfo { get => _DInfo; set { _DInfo = value; RaisePropertyChanged(nameof(DInfo)); } }
List<RInfo> _RInfo;
public List<RInfo> RInfo { get => _RInfo; set { _RInfo = value; RaisePropertyChanged(nameof(RInfo)); RaisePropertyChanged(nameof(RInfoDetailedList)); } }
public IEnumerable<RInfo_Details> RInfoDetailedList
{
get
{
IEnumerable<RInfo_Details> newlist = (from r in RInfo
join d in DInfo
on r.DCode equals d.DCode
select new
{
r.RCode,
r.ACode,
r.DCode,
d.DName,
d.DCExpires,
r.RNo,
}).ToList() as IEnumerable<RInfo_Details>;
return newlist as IEnumerable<RInfo_Details>;
}
}
public void iniTestData()
{
this.DInfo = new List<DInfo>()
{
new DInfo{DCode=1, DCExpires=false, DName="PAN" },
new DInfo{DCode=2, DCExpires=true, DName="FSSAI" },
new DInfo{DCode=3, DCExpires=false, DName="AANDAHR" }
};
this.RInfo = new List<RInfo>()
{
new RInfo{RCode=1, ACode=1, DCode=1, RNo="PAN NO ACode 1" },
new RInfo{RCode=2, ACode=2, DCode=1, RNo="PAN NO ACode 2" },
new RInfo{RCode=3, ACode=5, DCode=3, RNo="AADHAR ACode 5" },
new RInfo{RCode=4, ACode=4, DCode=1, RNo="PAN NO ACode 4" }
};
}
}
Here is code in XAML that is binding AppData.RInfoDetailedList
<ListView ItemsSource="{Binding Source={x:Static local:AppData.Instance}, Path=RInfoDetailedList}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding RNo}"/>
<Label Grid.Column="1" Content="{Binding DName}"/>
<Label Grid.Column="2" Content="{Binding DCExpires }"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Data from RInfo & DInfo Binds succesfully. but from RInfoDetailedList does not bind.
What is that I'm missing
This is because RInfoDetailedList is returning null due to the wrong cast!
You can create the RInfo_Details object in the select statement like this
public IEnumerable<RInfo_Details> RInfoDetailedList
{
get
{
if (RInfo == null || DInfo == null) return null;
var newList = from r in RInfo
join d in DInfo
on r.DCode equals d.DCode
select new RInfo_Details {
RCode = r.RCode,
ACode = r.ACode,
DCode = r.DCode,
DName = d.DName,
RNo = r.RNo,
};
return newList;
}
}

WPF: How to notify the modification of an interface that changes in a DLL on the bindings side

How to notify the modification of an interface that changes in a DLL on the bindings side.
To explain:
Dll code is not editable:
public interface IPlayer
{
int Id { get; }
string Name { get; }
Settings Settings { get; }
PlayerCategory Category { get; }
}
public class TennisPlayer: IPlayer
{
public virtual int Id { get; }
public virtual string Name { get; set; }
public Tennisman(int id, string name)
{
Id = id;
Name = name;
}
public Settings Settings { get; set; }
public PlayerCategory Category { get; set; }
}
My code:
public partial class PlayerItem : NotifyUserControl
{
private DispatcherTimer timer = new DispatcherTimer();
public static readonly DependencyProperty PlayerProperty =
DependencyProperty.Register("Player", typeof(IPlayer),
typeof(PlayerItem),
new PropertyMetadata(null, OnCaptionPropertyChanged));
public IPlayer Player
{
get { return (IPlayer)GetValue(PlayerProperty); }
set
{
SetValue(PlayerProperty, value);
}
}
public string PlayerName
{
get => Player != null ? Player.Name : "";
set => OnPropertyChanged();
}
public PlayerItem()
{
InitializeComponent();
timer.Interval = new TimeSpan(0, 0, 4);
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
OnPropertyChanged(nameof(PlayerName));
}
The external dll gives me player classes( for example: tennisman, footballer...) based on the same interface.
But I don't know it and I don't have to do class by class.
I must be missing something huge, but I managed to find nothing in my research.

How to return a string in the dataGrid.ItemsSource when it contains a ICollection<Continent>

When im displaying my database table it contains a few collections and those dont display in the datagrid. a column is generated but it stays blank. I do not know where to implement this either.
Maybe this will help also to explain what i want to display in the column instead of the ICollection
static void Main(string[] args)
{
using (var db = new Whataboutthisfish())
{
Vis vis = db.Vissen.Find(2);
if(vis != null)
{
if(vis.Continenten != null)
{
string s = "";
if(vis.Continenten.Count() > 1)
{
var continentenLijst = vis.Continenten;
s = continentenLijst.First().Naam;
foreach (Continent c in vis.Continenten)
{
s += ", "+c.Naam;
}
}
else
{
s = vis.Continenten.First().Naam;
}
Console.WriteLine(s);
}
}
}
}
I would like to return 1 string containing each continent's name using a seperator.
Like: "North-America" if there's only 1 continent in the collection
Or: "North-America, South-America" and so on for multiple.
Classes;
[Table("Continenten")]
public partial class Continent
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Continent()
{
Vissen = new HashSet<Vis>();
}
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Naam { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Vis> Vissen { get; set; }
}
}
[Table("Vissen")]
public partial class Vis
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Vis()
{
Continenten = new HashSet<Continent>();
Verbanden = new HashSet<Verband>();
Waterlagen = new HashSet<Waterlaag>();
}
public int Id { get; set; }
[StringLength(200)]
public string Naam { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Continent> Continenten { get; set; }
}
The Xaml for grid:
<DataGrid x:Name="dataGrid" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="0.833,0.846" Margin="10,10,10,10"/>
In code behind:
using (var db = new Catattafish.Whataboutthisfish())
{
dataGrid.ItemsSource = db.Vissen.ToList();
}

How to solve a HasMany relation in MVVM to show up in one row in a datagrid

i have a class Auswahl with some plain properties and a property RefFilters of type
List<RefAuswahlFilter>
What i want to achieve is: Display all Auswahl Properties in a datagrid with all RefFilter items in ONE row. the Problem is, the count of RefFilter is different from auswahl to auswahl object. in the past i use a datatable as the collection source. there i just added the MAX reffilters count as columns.
now i want to achieve this without a datatable, with something like "dynamic" Properties or anything.
public class Auswahl
{
public Auswahl()
{
this.RefFilters = new List<RefAuswahlFilter>();
}
public virtual string Beschreibung {get; set; }
public virtual long Id { get; set; }
public virtual string Programm { get; set; }
public virtual string Returnkey { get; set; }
public virtual string Variante { get; set; }
//RefFilters contains a Rank and a Filter Property
public virtual IList<RefAuswahlFilter> RefFilters { get; set; }
public class AuswahlVM
{
...
public ObservableCollection<Auswahl> Auswahlliste { get; private set; }
public void FillList()
{
try
{
var l = session.CreateCriteria(typeof(Auswahl)).List<Auswahl>().Where(x =>!String.IsNullOrEmpty(x.Returnkey));
this.Auswahlliste = new ObservableCollection<Auswahl>(l);
}
catch (Exception ex)
{
}
}
well i ended up in creating a helper class with an indexer and map my original list in that helper class
public class RefAuswahlFilterListe
{
private IList<RefAuswahlFilter> refFilters;
private Auswahl auswahl;
public RefAuswahlFilterListe(Auswahl refauswahl, IList<RefAuswahlFilter> filter)
{
this.refFilters = filter;
this.auswahl = refauswahl;
}
public string this[string rank]
{
get
{
long index;
if(Int64.TryParse(rank, out index))
{
var result = this.refFilters.FirstOrDefault(x => x.Filterrank == index);
return result != null ? result.Filter : String.Empty;
}
return String.Empty;
}
set
{
long index;
if (Int64.TryParse(rank, out index))
{
var result = this.refFilters.FirstOrDefault(x => x.Filterrank == index);
if(result == null)
this.refFilters.Add(new RefAuswahlFilter(){Auswahl = auswahl,Filter = value, Filterrank = index});
else
result.Filter = value;
}
}
}
}
<DataGridTextColumn Header="Filter1"
ToolTipService.ToolTip="Filter Spalte"
Binding="{Binding Path=Filter[1]}">
</DataGridTextColumn>
<DataGridTextColumn Header="Filter2"
ToolTipService.ToolTip="Filter Spalte"
Binding="{Binding Path=Filter[2]}"/>
i really dont know if this is the way to go. and i still has the problem to create wpf DataGridTextColumns dynamic (maybe in code behind?) cause it must be at least so much Columns like the highest count of RefFilters.

WPF ComboBox Binding

So I have the following model:
public class Person
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Address { get; set; }
public String EMail { get; set; }
public String Phone { get; set; }
}
public class Order
{
public Person Pers { get; set;}
public Product Prod { get; set; }
public List<Person> AllPersons { get; set; }
public Order(Person person, Product prod )
{
this.Pers = person;
this.Prod = prod;
AllPersons = database.Persons.GetAll();
}
}
And I have a WPF window used to edit an order.
I set the DataContext to Order.
public SetDisplay(Order ord)
{
DataContext = ord;
}
I have the following XAML:
<ComboBox Name="myComboBox"
SelectedItem = "{Binding Path=Pers, Mode=TwoWay}"
ItemsSource = "{Binding Path=AllPersons, Mode=OneWay}"
DisplayMemberPath = "FirstName"
IsEditable="False" />
<Label Name="lblPersonName" Content = "{Binding Path=Pers.FirstName}" />
<Label Name="lblPersonLastName" Content = "{Binding Path=Pers.LastName}" />
<Label Name="lblPersonEMail" Content = "{Binding Path=Pers.EMail}" />
<Label Name="lblPersonAddress" Content = "{Binding Path=Pers.Address}" />
However, the binding does not seem to work.......When I change the selected item , the labels do not update ....
Regards!!
Any reply is appreciated !!
Your model will need to fire change notifications. See INotifyPropertyChanged and INotifyCollectionChanged.
For INotifyPropertyChanged, you could use a base ViewModel class such as this one. For collections, ObservableCollection<T> does the hard work for you. However, in your case your collection won't change after the UI is bound to it, so you shouldn't need an observable collection. Regardless, I'd generally recommend using observable collections in your view model layer to save head-scratching should the code ever change.
An example of what this would look like is:
public class Person : ViewModel
{
private string firstName;
private string lastName;
private string email;
private string phone;
public string FirstName
{
get
{
return this.firstName;
}
set
{
if (this.firstName != value)
{
this.firstName = value;
OnPropertyChanged(() => this.FirstName);
}
}
}
public string LastName
{
get
{
return this.lastName;
}
set
{
if (this.lastName != value)
{
this.lastName = value;
OnPropertyChanged(() => this.LastName);
}
}
}
// and so on for other properties
}
public class Order : ViewModel
{
private readonly ICollection<Person> allPersons;
private Person pers;
private Product prod;
public Person Pers
{
get
{
return this.pers;
}
set
{
if (this.pers != value)
{
this.pers = value;
OnPropertyChanged(() => this.Pers);
}
}
}
public Product Prod
{
get
{
return this.prod;
}
set
{
if (this.prod != value)
{
this.prod = value;
OnPropertyChanged(() => this.Prod);
}
}
}
// no need for setter
public ICollection<Person> AllPersons
{
get
{
return this.allPersons;
}
}
public Order(Person person, Product prod )
{
this.Pers = person;
this.Prod = prod;
// no need for INotifyCollectionChanged because the collection won't change after the UI is bound to it
this.allPersons = database.Persons.GetAll();
}
}

Resources