I cant figureout why my ICollectionView is not refreshed. Can anyone explain what I'm doing wrong?
I've made a viewmodel like this:
class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Entity> m_entities = new ObservableCollection<Entity>();
public ICollectionView EntitiesView { get; private set; }
public ICollectionView HighCountView { get; private set; }
public ViewModel()
{
m_entities.Add(new Entity() { Id = 1, Name = "Erik", Description = "The first" });
m_entities.Add(new Entity() { Id = 2, Name = "Olle", Description = "The second" });
m_entities.Add(new Entity() { Id = 3, Name = "Kim", Description = "The last" });
EntitiesView = CollectionViewSource.GetDefaultView(m_entities);
EntitiesView.CurrentChanged += new EventHandler(EntitiesView_CurrentChanged);
HighCountView = new CollectionView(m_entities);
using (HighCountView.DeferRefresh())
{
HighCountView.Filter = e => ((Entity)e).Count > 3;
}
}
private void EntitiesView_CurrentChanged(object sender, EventArgs e)
{
Entity current = EntitiesView.CurrentItem as Entity;
if(current!=null)
{
current.Count++;
HighCountView.Refresh(); // Do I need this line?
OnPropertyChanged("HighCountView"); // or this?
}
}
...and in my window I use it as the datacontext, like this:
public partial class MainWindow : Window
{
private ViewModel vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = vm;
}
}
...and I'm doing my bindings in the XAML-code like this:
<ListBox Grid.Column="0" x:Name="listView1" DisplayMemberPath="Name" ItemsSource="{Binding EntitiesView}" IsSynchronizedWithCurrentItem="True" />
<ListView Grid.Column="1" x:Name="listView2" DisplayMemberPath="Name" ItemsSource="{Binding HighCountView}" IsSynchronizedWithCurrentItem="True" />
The problem is that all three Entities is always shown in listView2 despite that I set the Filter-property. Why?
EDIT
To made the sample complete, here is the Entity-class.
class Entity : INotifyPropertyChanged
{
private int m_id;
public int Id
{
bla bla.....
}
private string m_name;
public string Name
{
bla bla.....
}
private string m_description;
public string Description
{
bla bla.....
}
private int m_count;
public int Count
{
get { return m_count; }
set
{
if (value != m_count)
{
m_count = value;
OnPropertyChanged("Count");
}
}
}
public void Update()
{
Description = "Updated: " + (++Count).ToString() + " times.";
}
At last I found what was wrong.
If I change the line:
HighCountView = new CollectionView(m_entities);
to this
HighCountView = new ListCollectionView(m_entities);
then it works a expected.
I can also remove this line
OnPropertyChanged("HighCountView"); // or this?
I hope this can help somebody!
Related
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.
Im building a WPF application using MVVM Pattern but i 've been testing even for simple binding and i dont get it work the code is basically like this:
I think that the problem is in the xaml Bindings
Model:
public class Product
{
private string modelNumber;
public string ModelNumber
{
get { return modelNumber; }
set { modelNumber = value; }
}
private string modelName;
public string ModelName
{
get { return modelName; }
set { modelName = value; }
}
private decimal unitCost;
public decimal UnitCost
{
get { return unitCost; }
set { unitCost = value; }
}
private string description;
public string Description
{
get { return description; }
set { description = value; }
}
public Product(string modelNumber, string modelName, decimal unitCost, string description)
{
ModelNumber = modelNumber;
ModelName = modelName;
UnitCost = unitCost;
Description = description;
}
public static Product GetProduct()
{
return new Product("1","A6",20000,"Description");
}
}
ViewModel:
class ProductViewModel
{
public Product p;
public ProductViewModel()
{
p= Product.GetProduct();
}
}
Xaml:
<Grid Name="gridProductDetails" >
<TextBlock Margin="7">Model Number:</TextBlock>
<TextBox Margin="5" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding p.ModelNumber}" ></TextBox>
</Grid>
Code Behind:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
gridProductDetails.DataContext = new ProductViewModel();
}
Please update the following that should solve your problem:
Mark ProductViewModel as public
Convert variable p to auto property. public Product p { get; set; }
Regards
I am using ObservableCollection as an ItemSource for my listBox component:
But the behavior of the control is not proper as for me. The matter I have scroll down to ths first occurence in my collection, but not last.
The sample list is: 1,1,2,3,4,5,6,7,8,9,11,22,33,1
When you enetr last 1 you component scroll up to first 1 :). This not what I am wating for.
Please advise. Here a code of component:
public class MyListBox : ListBox
{
protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
if (Items.Count > 0)
{
var item = Items[Items.Count - 1];
UpdateLayout();
ScrollIntoView(item);
UpdateLayout();
}
}
}
Sorry but it needs to be a class as a List or OC is going to really do a value comparison. So you need to make identical values unique. I test this out and it works.
<StackPanel Orientation="Vertical" >
<ListBox x:Name="lbStringList" ItemsSource="{Binding Path=UniqueStringList}" DisplayMemberPath="Str" Height="60" VerticalAlignment="Top"/>
<Button Click="Button_Click" Content="56" />
</StackPanel>
private List<UniqueString> uniqueStringList = new List<UniqueString>()
{
new UniqueString("zero",0),
new UniqueString("one",1),
new UniqueString("two",2),
new UniqueString("three",3),
new UniqueString("four",4),
new UniqueString("five",5),
new UniqueString("six",6),
new UniqueString("seven",7),
new UniqueString("zero",8)
};
public MainWindow()
{
InitializeComponent();
}
public List<string> StringList { get { return new List<string>() { "one", "two", "three", "four", "five", "one" }; } }
public List<UniqueString> UniqueStringList
{
get
{
return uniqueStringList;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(sender.GetHashCode());
lbStringList.ScrollIntoView(lbStringList.Items[8]);
}
public class UniqueString
{
private Int32 id;
public string Str { get; private set; }
public override bool Equals(object obj)
{
UniqueString item = (UniqueString)obj;
return item.id == id;
}
public override int GetHashCode()
{
return id;
}
public UniqueString(string str, Int32 _id) { Str = str; id = _id; }
}
I have a ViewModel that sets the value for the "UserStructure" property. The problem is that the combobox wont bind to the value.
public class OwnerOccupierAccountViewModel : ViewModelBase
{
/// <summary>
/// Load combobox structures
/// </summary>
private readonly LoadOperation<Structure> _loadStructures;
private readonly LoadOperation<UnitOccupierDetail> _loadUnitOccupierDetails;
//public ICommand SaveAccountSettingsCommand { get; set; }
#region Properties
private ObservableCollection<Structure> _structures;
public ObservableCollection<Structure> Structures
{
get { return _structures; }
set
{
_structures = value;
RaisePropertyChanged("Structures");
}
}
private Structure _userStructure;
public Structure UserStructure
{
get { return _userStructure; }
set
{
_userStructure = value;
RaisePropertyChanged("SelectedStructure");
}
}
private UnitOccupierDetail _unitOccupierDetail;
public UnitOccupierDetail UnitOccupierDetail
{
get { return _unitOccupierDetail; }
set
{
_unitOccupierDetail = value;
RaisePropertyChanged("UnitOccupierDetail");
}
}
#endregion
public OwnerOccupierAccountViewModel()
{
// SaveAccountSettingsCommand = new DelegateCommand(SaveAccountSettings, CanSave);
UserAccountContext _userAccountContext;
if (!DesignerProperties.IsInDesignTool)
{
var loggedInUser = new Guid(WebContext.Current.User.UserID.ToString());
_userAccountContext = new UserAccountContext();
#region load structures
_loadStructures = _userAccountContext.Load(_userAccountContext.GetStructuresQuery());
_loadStructures.Completed += _loadStructuresCompleted;
#endregion
#region load user data
_loadUnitOccupierDetails =
_userAccountContext.Load(
_userAccountContext.GetUnitOccupierDetailsQuery().Where(
u => u.UserIDFK == loggedInUser && u.StructureFK == 92));
_loadUnitOccupierDetails.Completed += _loadUnitOccupierDetails_Completed;
#endregion
}
}
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
_unitOccupierDetail= new UnitOccupierDetail();
_unitOccupierDetail = _loadUnitOccupierDetails.Entities.First();
_userStructure = _unitOccupierDetail.Structure;
}
void _loadStructuresCompleted(object sender, EventArgs e)
{
var theseStructures = new ObservableCollection<Structure>(_loadStructures.Entities);
Structures = theseStructures;
}
//private void SaveAccountSettings(object param)
//{
//}
//private static bool CanSave(object param)
//{
// return true;
//}
}
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
DisplayMemberPath='StructureName'
SelectedValuePath='IDStructure'
SelectedItem='{Binding SelectedStructure,Mode=TwoWay}'
Width="200"
Height="25">
in xaml UserStructure instead SelectedStructure.
In your XAML SelectedItem is bound to SelectedStructure instead of UserStructure what you want
UPDATE:
Your code doesn't work because you should set to SelectedItem object that has reference equality with some object in ItemsSource. In your ViewModel you set Structures as result of one service operation and UserStructure as result of the other. UserStructure and some object in Structures can be equals (object.Equals) but not reference equals (object.ReferenceEquals). ComboBox like other ItemsControls doesn't compare items by equality, it compares they by identity. So, to have right code you should select from Structures object that equals to UserStructure and set it as UserStructure:
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
...
Structure userStructure = Structures.FirstOrDefault(s=>s.Equals(_unitOccupierDetail.Structure));
UserStructure = userStructure;
}
In this case you should be sure that Structures comes before. You can look at Reactive Extensions Observable.ForkJoin() method to syncronize 2 async calls.
Try such changes
public class OwnerOccupierAccountViewModel : ViewModelBase
{
/// <summary>
/// Load combobox structures
/// </summary>
private readonly LoadOperation<Structure> _loadStructures;
private readonly LoadOperation<UnitOccupierDetail> _loadUnitOccupierDetails;
//public ICommand SaveAccountSettingsCommand { get; set; }
#region Properties
private ObservableCollection<Structure> _structures;
public ObservableCollection<Structure> Structures
{
get { return _structures; }
set
{
_structures = value;
RaisePropertyChanged("Structures");
}
}
private Structure _userStructure;
public Structure UserStructure
{
get { return _userStructure; }
set
{
_userStructure = value;
RaisePropertyChanged("UserStructure");
}
}
private UnitOccupierDetail _unitOccupierDetail;
public UnitOccupierDetail UnitOccupierDetail
{
get { return _unitOccupierDetail; }
set
{
_unitOccupierDetail = value;
RaisePropertyChanged("UnitOccupierDetail");
}
}
#endregion
public OwnerOccupierAccountViewModel()
{
// SaveAccountSettingsCommand = new DelegateCommand(SaveAccountSettings, CanSave);
UserAccountContext _userAccountContext;
if (!DesignerProperties.IsInDesignTool)
{
var loggedInUser = new Guid(WebContext.Current.User.UserID.ToString());
_userAccountContext = new UserAccountContext();
#region load structures
_loadStructures = _userAccountContext.Load(_userAccountContext.GetStructuresQuery());
_loadStructures.Completed += _loadStructuresCompleted;
#endregion
#region load user data
_loadUnitOccupierDetails =
_userAccountContext.Load(
_userAccountContext.GetUnitOccupierDetailsQuery().Where(
u => u.UserIDFK == loggedInUser && u.StructureFK == 92));
_loadUnitOccupierDetails.Completed += _loadUnitOccupierDetails_Completed;
#endregion
}
}
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
_unitOccupierDetail= new UnitOccupierDetail();
_unitOccupierDetail = _loadUnitOccupierDetails.Entities.First();
_userStructure = _unitOccupierDetail.Structure;
}
void _loadStructuresCompleted(object sender, EventArgs e)
{
var theseStructures = new ObservableCollection<Structure>(_loadStructures.Entities);
Structures = theseStructures;
}
//private void SaveAccountSettings(object param)
//{
//}
//private static bool CanSave(object param)
//{
// return true;
//}
}
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
DisplayMemberPath='StructureName'
SelectedValuePath='IDStructure'
SelectedItem='{Binding UserStructure,Mode=TwoWay}'
Width="200"
Height="25">
Updated:
Hmm, maybe try changes here:
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
SelectedItem='{Binding UserStructure, Mode=TwoWay}'
Width="200"
Height="25">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StructureName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
i have big problem with binding Stacked Column Series to my chart.
I have
public ObservableCollection Series property in my ViewModel and try by many ways but it still not working.
This is code from ViewModel to prepare Series:
private void drawChart()
{
this.Series.Clear();
var dataValues = new List<List<SimpleDataValue>>();
int wartoscNiezalezna = 1;
for (int i = 0; i < 2; i++)
{
dataValues.Add(new List<SimpleDataValue>());
}
foreach (var item in myCollection)
{
var param = someparam;
dataValues[0].Add(new SimpleDataValue { IndependentValue = "Czujnik " + wartoscNiezalezna, DependentValue = 100 });
//czerwone
dataValues[1].Add(new SimpleDataValue { IndependentValue = "" + wartoscNiezalezna, DependentValue = 200 });
wartoscNiezalezna++;
}
var stackedSeries = Activator.CreateInstance(typeof(StackedColumnSeries)) as DefinitionSeries;
int itemnr=0;
foreach (var item in dataValues)
{
var definicja = new SeriesDefinition();
if(itemnr==0)
definicja.Title = "Stan 1";
else
definicja.Title = "Stan 2";
definicja.DependentValuePath = "DependentValue";
definicja.IndependentValuePath = "IndependentValue";
definicja.ToolTip = "asdas";
definicja.ItemsSource = item;
stackedSeries.SeriesDefinitions.Add(definicja);
itemnr++;
}
Series.Add(stackedSeries);
}
I cant bind it to:
<charting:Chart x:Name="MyChart" Padding="10,10,10,10">
<charting:Chart.Series>
<charting:StackedColumnSeries>
<charting:SeriesDefinition ItemsSource="{Binding Series}" DependentValuePath="DependentValue" IndependentValuePath="IndependentValue">
</charting:SeriesDefinition>
</charting:StackedColumnSeries>
</charting:Chart.Series>
</charting:Chart>
I was trying with SeriesDefinitions Collection and others.
I will be very grateful to some help.
I hope I've answered your question there
Anyway I post the second part of my answer here:
MainWindow.xaml:
<charting:Chart x:Name="MyChart" Padding="10,10,10,10">
<charting:Chart.Series>
<charting:StackedColumnSeries>
<charting:SeriesDefinition Title="Stan 1" ItemsSource="{Binding FirstCollection}" DependentValuePath="DependentValue" IndependentValuePath="IndependentValue" />
<charting:SeriesDefinition Title="Stan 2" ItemsSource="{Binding SecondCollection}" DependentValuePath="DependentValue" IndependentValuePath="IndependentValue" />
</charting:StackedColumnSeries>
</charting:Chart.Series>
</charting:Chart>
MainWindow.xaml.cs
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.MyChart.DataContext = new ChartModel
{
FirstCollection = Enumerable.Range(1, 10).Select(i => new SimpleDataValue { IndependentValue = "Czujnik " + i, DependentValue = 100 }).ToList(),
SecondCollection = Enumerable.Range(1, 10).Select(i => new SimpleDataValue { IndependentValue = "" + i, DependentValue = 200 }).ToList()
};
}
}
public class SimpleDataValue
{
public string IndependentValue { get; set; }
public int DependentValue { get; set; }
}
public class ChartModel
{
public List<SimpleDataValue> FirstCollection { get; set; }
public List<SimpleDataValue> SecondCollection { get; set; }
}
I am not sure with the syntax but the logic should be like below:
ViewModel
public class GraphItem {
public string IndependentValue { get; set; }
public int DependentValue1 { get; set; }
public int DependentValue2 { get; set; }
}
public class ChartViewModel
{
private List<GraphItem> itemCollection;
public List<GraphItem> ItemCollection
{
get { return itemCollection;}
set {
itemCollection=value;
OnPropertyChanged("ItemCollection");
}
}
public ChartViewModel()
{
//Bind ItemCollection
}
}
Xaml:
<charting:Chart x:Name="MyChart" Padding="10,10,10,10" DataContext={Binding ItemCollection}">
<charting:Chart.Series>
<charting:StackedColumnSeries>
<charting:SeriesDefinition Title="Stan 1" ItemsSource="{Binding}" DependentValuePath="DependentValue1" IndependentValuePath="IndependentValue" />
<charting:SeriesDefinition Title="Stan 2" ItemsSource="{Binding}" DependentValuePath="DependentValue2" IndependentValuePath="IndependentValue" />
</charting:StackedColumnSeries>
</charting:Chart.Series>
</charting:Chart>
May this help.