Using Nested ObservableCollection's to make a ContextMenu WPF Window - wpf

Thanks for the help in advance. First time posting here.
I have included a bit of sample code. I would like to dynamically build a ContextMenu based on these custom objects(ObservableCollection). I can bind the ContextMenu to the first level of Team, but can you also bind a second level "ContextMenu? / MenuItem?" for the territories. I need to see the territories in a team when the team is highlighted.
My Team Object
class Team
{
private int _TeamProperty1 = 0;
private int _TeamProperty2 = 0;
ObservableCollection<Territory> _Territories = new ObservableCollection<Territory>();
public Team()
{
}
public ObservableCollection<Territory> Territories
{
get { return _Territories; }
set { _Territories = value; }
}
public int TeamProperty1
{
get { return _TeamProperty1; }
set { _TeamProperty1 = value; }
}
public int TeamProperty2
{
get { return _TeamProperty2; }
set { _TeamProperty2 = value; }
}
}
My Territory Object
class Territory
{
private int _TerritoryProperty1 = 0;
public int TerritoryProperty1
{
get { return _TerritoryProperty1; }
set { _TerritoryProperty1 = value; }
}
public void Method1()
{
//Do Some Work
}
}
Application
class MyApplication
{
ObservableCollection<Team> _Teams = new ObservableCollection<Team>();
ContextMenu _TeritorySwitcher = new ContextMenu();
public MyApplication()
{
AddTeam()
}
public void AddTeam()
{
Team _Team1 = new Team();
_Team1.Territories.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Territories_CollectionChanged);
_Teams.Add(_Team1);
Team _Team2 = new Team();
_Team2.Territories.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Territories_CollectionChanged);
_Teams.Add(_Team2);
Team _Team3 = new Team();
_Team3.Territories.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Territories_CollectionChanged);
_Teams.Add(_Team3);
foreach (Team t in _Teams)
{
t.Territories.Add(new Territory());
t.Territories.Add(new Territory());
t.Territories.Add(new Territory());
t.Territories.Add(new Territory());
}
}
void Territories_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
ObservableCollection<Team> _TeamsSort = new ObservableCollection<Team>(_Teams.OrderBy(tm => tm.TeamProperty1));
_TeritorySwitcher.ItemsSource = _TeamsSort;
_TeritorySwitcher.DisplayMemberPath = "TeamProperty2";
}
}
Now my ContextMenu shows the teams (3 of them), but I can't figure out how to also show the Territories (There should be 4 in each team)

Ok, I figured it out. Thanks for the direction. Here is the new MyApplication Class. In the sample I don't have any data but you can fill that in if you need to see this work. It's just a sample framework.
class MyApplication
{
ObservableCollection<Team> _Teams = new ObservableCollection<Team>();
ContextMenu _TeritorySwitcher = new ContextMenu();
public MyApplication()
{
}
public void AddTeam()
{
_Teams.Add(new Team());
_Teams.Add(new Team());
_Teams.Add(new Team());
_Teams.Add(new Team());
foreach (Team t in _Teams)
{
t.Territories.Add(new Territory());
t.Territories.Add(new Territory());
t.Territories.Add(new Territory());
}
SetContextMenu();
}
private void SetContextMenu()
{
HierarchicalDataTemplate _hdtTerritories = new HierarchicalDataTemplate();
_hdtTerritories.DataType = typeof(Territory);
HierarchicalDataTemplate _hdtTeams = new HierarchicalDataTemplate();
_hdtTeams.DataType = typeof(Team);
FrameworkElementFactory _TeamFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TeamFactory.Name = "txtTeamInfo";
_TeamFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TeamProperty1"));
FrameworkElementFactory _TerritoryFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TerritoryFactory.Name = "txtTerritoryInfo";
_TerritoryFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TerritoryProperty1"));
_hdtTeams.ItemsSource = new Binding("Territories");
_hdtTeams.VisualTree = _TeamFactory;
_hdtTerritories.VisualTree = _TerritoryFactory;
_hdtTeams.ItemTemplate = _hdtTerritories;
_TeritorySwitcher.ItemTemplate = _hdtTeams;
_TeritorySwitcher.ItemsSource = this._Teams;
}
}

Related

.net c# WebChannelFactory keepAliveEnabled=false with webHttpBehavior

I create WebChannelFactory. I need set KeepAlive=false.
I found only one solution use CustomBinding. and set property keepAliveEnabled to false.
I use custom behavior for my factory also.
Code:
static CustomBinding GetBinding(MySettings serviceSettings = null)
{
var customBinding = new CustomBinding();
HttpTransportBindingElement transportBindingElement = new HttpTransportBindingElement();
transportBindingElement.KeepAliveEnabled = false;
transportBindingElement.MaxBufferSize = 0x07000000;
transportBindingElement.MaxReceivedMessageSize = 0x07000000;
if (serviceSettings != null)
{
customBinding.SendTimeout = serviceSettings.SendTimeout;
}
customBinding.Elements.Add(transportBindingElement);
return customBinding;
}
var customBinding = GetBinding(serviceSettings);
WebChannelFactory<TChannel> factory = new WebChannelFactory<TChannel>(customBinding, new Uri(url));
factory.Endpoint.Behaviors.Add(new MyWebHttpBehavior(userId));
class MyWebHttpBehavior : IEndpointBehavior
{
private int _userId;
public MyWebHttpBehavior(int userId)
{
_userId = userId;
}
public void AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior)
{
behavior.MessageInspectors.Add(new MyClientMessageInspector(_userId));
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{ }
public void Validate(ServiceEndpoint serviceEndpoint)
{ }
}
In current situation i get error "does not have a Binding with the None MessageVersion".

Handling events across different classes using C#

I have a property defined in Class A. When the property is changed then i need to raise an event. Another class B should respond to this event and do something. I have done something like this:
Code:
Class A{
string variable = "test";
public delegate void EventHandler(object sender, EventArgs args);
public static event EventHandler ThrowAddEvent = delegate { };
public static event EventHandler ThrowRemoveEvent = delegate { };
public String Name { get; set; }
public int Select { get; set; }
public Window Formref1 { get; set; }
public bool IsSelected
{
get { return IsSlctd; }
set
{
IsSlctd = value;
if (value)
{
ThrowAddEvent(this, new EventArgs());
}
else
{
ThrowRemoveEvent(this, new EventArgs());
}
}
}
}
Another class which is responding to this event is defined as follows:
Class B{
public B(ParsedResult _result)
{
InitializeComponent();
if (_result != null)
{
this.Result = _result;
PopulateColumns1();
DataGrid1.Items.Clear();
DataGrid1.ItemsSource = Populatevariables();
}
}
public void PopulateColumns1()
{
DataGridTextColumn Colvar = new DataGridTextColumn();
Colvar.Header = "Variables";
Colvar.Binding = new Binding("Name");
DataGrid1.Columns.Add(Colvar);
DataGridTemplateColumn Col = new DataGridTemplateColumn();
Col.Header = "Select";
DataTemplate dd = new DataTemplate();
FrameworkElementFactory FF = new FrameworkElementFactory(typeof(CheckBox));
FF.SetBinding(CheckBox.BindingGroupProperty, new Binding("Select"));
FF.SetBinding(CheckBox.IsCheckedProperty, new Binding("IsSelected") { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
FF.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center);
dd.VisualTree = FF;
Col.CellTemplate = dd;
DataGrid1.Columns.Add(Col);
}
private List<A> PopulateVariables()
{
CheckBox cb = new CheckBox();
List<A> CharList = new List<A>();
Result.GetCharacteristicList.MoveNext();
IEnumerator<A2LCharacteristic> enumeratorlist = Result.GetCharacteristicList;
for (int i = 0; enumeratorlist.MoveNext(); i++)
{
CharList.Add(new A() { Name = enumeratorlist.Current.Name, Select = i, Formref1 = this});
}
enumeratorlist.Reset();
return CharList;
}
private void OKBtn_Click(object sender, RoutedEventArgs e)
{
A Instance_A = new A();
Instance_A.ThrowAddEvent += (sender1, args) => { Addvrbl(Instance_A.variable); };
Instance_A.ThrowRemoveEvent += (sender2, args) => { RmvVrbl(Instance_A.variable); };
this.Close();
}
public void Addvrbl(string vrbl)
{
if (!(vrbllist.Contains(vrbl)))
{
vrbllist.Add(vrbl);
}
}
public void RmvVrbl(string vrbl)
{
if ((vrbllist.Contains(vrbl)))
{
vrbllist.Remove(vrbl);
}
}
}
The problem is it is not going inside the method "AddVrbl" and "RmvVrbl". I have used the solution from here. Please help.
OK, instead of subscribing to the event of a new instance of A,which will never get triggered/published. When you initializing CharList, you have to subscribe to the event of each A item.
Something like:
for (int i = 0; enumeratorlist.MoveNext(); i++)
{
var obja=new A() { Name = enumeratorlist.Current.Name, Select = i, Formref1 = this};
obja.ThrowAddEvent += ...
obja.ThrowRemoveEvent += ...
CharList.Add(obja);
}
PS: Make sure you un-subscribe these events before removing an item from your CharList.

why Dispatcher.invoke() not working?

whenever a change is occured changes i call this set_filelist_inventory(). and send a new list to this function but UI is not upadating.
public partial class Inventory : UserControl
{
public List<String> file_list = new List<String>();
public void set_filelist_inventory(List<string> x)
{
if (file_list.SequenceEqual(x)) { }
else
{
file_list = x;
Dispatcher.Invoke(() =>
{
listview1.ItemsSource = file_list;
});
}
}
public Inventory()
{
InitializeComponent();
file_list = General.GetFileList();
Discover d = new Discover();
d.send(d);
listview1.ItemsSource = file_list;
}
}
I tried removing if else block. it still didn't worked.

Transparency in animator control

I'm writing a user control for animation.
Its uses an internal ImageList to store the animation images and paint one after the other in a loop.
This is the whole code:
public partial class Animator : UserControl
{
public event EventHandler OnLoopElapsed = delegate { };
private ImageList imageList = new ImageList();
private Timer timer;
private bool looping = true;
private int index;
private Image newImage;
private Image oldImage;
public Animator()
{
InitializeComponent();
base.DoubleBuffered = true;
timer = new Timer();
timer.Tick += timer_Tick;
timer.Interval = 50;
}
public bool Animate
{
get { return timer.Enabled; }
set
{
index = 0;
timer.Enabled = value;
}
}
public int CurrentIndex
{
get { return index; }
set { index = value; }
}
public ImageList ImageList
{
set
{
imageList = value;
Invalidate();
index = 0;
}
get { return imageList; }
}
public bool Looping
{
get { return looping; }
set { looping = value; }
}
public int Interval
{
get { return timer.Interval; }
set { timer.Interval = value; }
}
private void timer_Tick(object sender, EventArgs e)
{
if (imageList.Images.Count == 0)
return;
Invalidate(true);
index++;
if (index >= imageList.Images.Count)
{
if (looping)
index = 0;
else
timer.Stop();
OnLoopElapsed(this, EventArgs.Empty);
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (oldImage != null)
e.Graphics.DrawImage(oldImage, ClientRectangle);
else
e.Graphics.Clear(BackColor);
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
if (imageList.Images.Count > 0)
{
newImage = imageList.Images[index];
g.DrawImage(newImage, ClientRectangle);
oldImage = newImage;
}
else
{
e.Graphics.Clear(BackColor);
}
}
}
The animation seems very nice and smooth,
but the problem is that its surrounding rectangle is painted black.
What am I missing here?
I've seen very smooth transparent animation done here in WPF,
I've placed some label behind it and they are seen thru the rotating wheel as I hoped.
But I don't know WPF well enough to build such a control in WPF.
Any idea or WPF sample code will be appreciated.
This was solved by removing this line from the constructor:
base.DoubleBuffered = true;
Now the control is fully transparent, even while changing its images.

Filter a collection with LINQ vs CollectionView

I want to filter a ObservableCollection with max 3000 items in a DataGrid with 6 columns. The user should be able to filter in an "&&"-way all 6 columns.
Should I use LINQ or a CollectionView for it? LINQ seemed faster trying some www samples. Do you have any pro/cons?
UPDATE:
private ObservableCollection<Material> _materialList;
private ObservableCollection<Material> _materialListInternal;
public MaterialBrowserListViewModel()
{
_materialListInternal = new ObservableCollection<Material>();
for (int i = 0; i < 2222; i++)
{
var mat = new Material()
{
Schoolday = DateTime.Now.Date,
Period = i,
DocumentName = "Excel Sheet" + i,
Keywords = "financial budget report",
SchoolclassCode = "1",
};
_materialListInternal.Add(mat);
var mat1 = new Material()
{
Schoolday = DateTime.Now.Date,
Period = i,
DocumentName = "Word Doc" + i,
Keywords = "Economical staticstics report",
SchoolclassCode = "2",
};
_materialListInternal.Add(mat1);
}
MaterialList = CollectionViewSource.GetDefaultView(MaterialListInternal);
MaterialList.Filter = new Predicate<object>(ContainsInFilter);
}
public bool ContainsInFilter(object item)
{
if (String.IsNullOrEmpty(FilterKeywords))
return true;
Material material = item as Material;
if (DocumentHelper.ContainsCaseInsensitive(material.Keywords,FilterKeywords,StringComparison.CurrentCultureIgnoreCase))
return true;
else
return false;
}
private string _filterKeywords;
public string FilterKeywords
{
get { return _filterKeywords; }
set
{
if (_filterKeywords == value)
return;
_filterKeywords = value;
this.RaisePropertyChanged("FilterKeywords");
MaterialList.Refresh();
}
}
public ICollectionView MaterialList { get; set; }
public ObservableCollection<Material> MaterialListInternal
{
get { return _materialListInternal; }
set
{
_materialListInternal = value;
this.RaisePropertyChanged("MaterialList");
}
}
Using ICollectionView gives you automatic collection changed notifications when you call Refresh. Using LINQ you'll need to fire your own change notifications when the filter needs to be re-run to update the UI. Not difficult, but requires a little more thought than just calling Refresh.
LINQ is more flexible that the simple yes/no filtering used by ICollectionView, but if you're not doing something complex there's not really any advantage to that flexibility.
As Henk stated, there shouldn't be a noticable performance difference in the UI.
For an interactive (DataGrid?) experience you should probabaly use the CollectionView. For a more code-oriented sorting, LINQ.
And with max 3000 items, speed should not be a (major) factor in a UI.
How about both? Thomas Levesque built a LINQ-enabled wrapper around ICollectionView.
Usage:
IEnumerable<Person> people;
// Using query comprehension
var query =
from p in people.ShapeView()
where p.Age >= 18
orderby p.LastName, p.FirstName
group p by p.Country;
query.Apply();
// Using extension methods
people.ShapeView()
.Where(p => p.Age >= 18)
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName)
.Apply();
Code:
public static class CollectionViewShaper
{
public static CollectionViewShaper<TSource> ShapeView<TSource>(this IEnumerable<TSource> source)
{
var view = CollectionViewSource.GetDefaultView(source);
return new CollectionViewShaper<TSource>(view);
}
public static CollectionViewShaper<TSource> Shape<TSource>(this ICollectionView view)
{
return new CollectionViewShaper<TSource>(view);
}
}
public class CollectionViewShaper<TSource>
{
private readonly ICollectionView _view;
private Predicate<object> _filter;
private readonly List<SortDescription> _sortDescriptions = new List<SortDescription>();
private readonly List<GroupDescription> _groupDescriptions = new List<GroupDescription>();
public CollectionViewShaper(ICollectionView view)
{
if (view == null)
throw new ArgumentNullException("view");
_view = view;
_filter = view.Filter;
_sortDescriptions = view.SortDescriptions.ToList();
_groupDescriptions = view.GroupDescriptions.ToList();
}
public void Apply()
{
using (_view.DeferRefresh())
{
_view.Filter = _filter;
_view.SortDescriptions.Clear();
foreach (var s in _sortDescriptions)
{
_view.SortDescriptions.Add(s);
}
_view.GroupDescriptions.Clear();
foreach (var g in _groupDescriptions)
{
_view.GroupDescriptions.Add(g);
}
}
}
public CollectionViewShaper<TSource> ClearGrouping()
{
_groupDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> ClearSort()
{
_sortDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> ClearFilter()
{
_filter = null;
return this;
}
public CollectionViewShaper<TSource> ClearAll()
{
_filter = null;
_sortDescriptions.Clear();
_groupDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> Where(Func<TSource, bool> predicate)
{
_filter = o => predicate((TSource)o);
return this;
}
public CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, true, ListSortDirection.Ascending);
}
public CollectionViewShaper<TSource> OrderByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, true, ListSortDirection.Descending);
}
public CollectionViewShaper<TSource> ThenBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, false, ListSortDirection.Ascending);
}
public CollectionViewShaper<TSource> ThenByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, false, ListSortDirection.Descending);
}
private CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector, bool clear, ListSortDirection direction)
{
string path = GetPropertyPath(keySelector.Body);
if (clear)
_sortDescriptions.Clear();
_sortDescriptions.Add(new SortDescription(path, direction));
return this;
}
public CollectionViewShaper<TSource> GroupBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
string path = GetPropertyPath(keySelector.Body);
_groupDescriptions.Add(new PropertyGroupDescription(path));
return this;
}
private static string GetPropertyPath(Expression expression)
{
var names = new Stack<string>();
var expr = expression;
while (expr != null && !(expr is ParameterExpression) && !(expr is ConstantExpression))
{
var memberExpr = expr as MemberExpression;
if (memberExpr == null)
throw new ArgumentException("The selector body must contain only property or field access expressions");
names.Push(memberExpr.Member.Name);
expr = memberExpr.Expression;
}
return String.Join(".", names.ToArray());
}
}
Credit:
http://www.thomaslevesque.com/2011/11/30/wpf-using-linq-to-shape-data-in-a-collectionview/
Based on a visual complexity and number of items there really WILL be a noticable performance difference since the Refresh method recreates the whole view!!!
You need my ObservableComputations library. Using this library you can code like this:
ObservableCollection<Material> MaterialList = MaterialListInternal.Filtering(m =>
String.IsNullOrEmpty(FilterKeywords)
|| DocumentHelper.ContainsCaseInsensitive(
material.Keywords, FilterKeywords, StringComparison.CurrentCultureIgnoreCase));
MaterialList reflects all the changes in the MaterialListInternal collection. Do not forget to add the implementation of the INotifyPropertyChanged interface to Material class, so that MaterialList collection reflects the changes in material.Keywords property.

Resources