Is it Possible to add Hide the one property based on other property? - wpf

In my custom control I have 3 properties(State,Value,Count), State property with Enum(Dock,Float,Tab), if the Enum value(Float) means I want to Hide(Browsable False) Value property in WPF. Is there any possibility to do this with PropertyChanged of the State.

In the setter for State, just check what the value is. If it's Float hide, else unhide.
private StateEnum _state;
public StateEnum State {
get { return _state; }
set
{
if (value == StateEnum.Float)
{
// Hide stuff
}
else
{
// Show stuff
}
name = value;
}
}

Normally you cannot hide a property conditionally.
However, there is a trick.
You can define a value interface and wrapper for your value property and implement for value and nonvalue states.
Example:
public interface IValueWrapper {}
public class BasicValueWrapper : IValueWrapper
{
public int Value { get; set; }
}
public class NoneValueWrapper : IValueWrapper {}
Main Class :
public class MainClass
{
private StateEnum _state;
public StateEnum State {
get { return _state; }
set
{
if (value == StateEnum.Float)
{
// Hide stuff
this.Value= new NoneValueWrapper();
}
else
{
// Show stuff
this.Value= new BasicValueWrapper();
}
name = value;
}
}
public IValueWrapper Value { get; set; }
}

Related

WPF binding CombobBoxItem base on property

My View Model class:
class Student : INotifyPropertyChanged
{
private string name;
private bool isVisible;
public event PropertyChangedEventHandler PropertyChanged;
public string PersonName
{
get { return name; }
set
{
name = value;
OnPropertyChanged("PersonName");
}
}
public bool IsVisible
{
get { return isVisible; }
set
{
isVisible = value;
OnPropertyChanged("IsVisible");
}
}
}
My Students collection that store all my objects:
public ObservableCollection<Student> Students { get; set; }
XAML:
<ComboBox x:Name="cbStudents"
ItemsSource="{Binding Students}"
SelectionChanged="cbInterfaces_SelectionChanged"/>
So in some point i want to disappear several Students from my ComboBox so i just change IsVisible value to False.
Any idea how to do that using XAML ?
You can have your Students collection return only visible students.
//All students (visible and invisible)
ObservableCollection<Students> _AllStudents = GetAllStudentsFromDataSource();
//only visible students
ObservableCollection<Students> _VisibleStudents = new ObservableCollection<Students>();
foreach(var _s in _AllStudents.Where(x => x.IsVisible)){
_VisibleStudents.Add(_s);
}
//your property
public ObservableCollection<Student> Students { get{ return _VisibleStudents; } }
In the case of your check box toggling the visibility of students, your checkbox can be bound to a command like this:
<Checkbox IsChecked="{Binding IsCheckboxChecked}" Command={Binding ToggleStudents}" />
And your view model has an extra control for the checkbox toggle and the command:
bool _IsCheckboxChecked = false;
public bool IsCheckboxChecked {
get { return _IsCheckboxChecked;}
set {
if(_IsCheckboxChecked != value)
{
_IsCheckboxChecked = value;
}
}
}
public ICommand ToggleStudents
{
get;
internal set;
}
private void ToggleStudentsCommand()
{
ToggleStudents = new RelayCommand(ToggleStudentsExecute);
}
public void ToggleStudentsExecute()
{
_VisibleStudents.Clear();
if(_IsCheckboxChecked){
foreach(var _s in _AllStudents.Where(x => x.IsVisible)){
_VisibleStudents.Add(_s);
}
}
else
{
foreach(var _s in _AllStudents.Where(x => x.IsVisible == false)){
_VisibleStudents.Add(_s);
}
}
OnPropertyChanged("Students");
}
Your xaml doesn't need to change.

Exposing custom properties using UI Automation Framework

Given a very basic WinForms custom/user control, using System.Windows.Automation it is possible to manipulate built in properties for the custom control.
This is done like this:
public object GetPropertyValue(int propertyId)
{
if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
{
return "Hello World!";
}
}
What I would like to do is expose custom properties to ui automation such as ReadyState, LastAccessed, Etc.
Is this possible?
No, you can't extend the list of properties, and this is complicated by the fact you use Winforms that has a poor UI Automation support (it uses IAccessible with bridges etc.).
What you can do though is add some fake objects to the automation tree, for example, here is a sample Winforms UserControl that does it:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
Button button = new Button();
button.Location = new Point(32, 28);
button.Size = new Size(75, 23);
button.Text = "MyButton";
Controls.Add(button);
Label label = new Label();
label.Location = new Point(49, 80);
label.Size = new Size(35, 13);
label.Text = "MyLabel";
Controls.Add(label);
MyCustomProp = "MyCustomValue";
}
public string MyCustomProp { get; set; }
protected override AccessibleObject CreateAccessibilityInstance()
{
return new UserControl1AccessibleObject(this);
}
protected class UserControl1AccessibleObject : ControlAccessibleObject
{
public UserControl1AccessibleObject(UserControl1 ownerControl)
: base(ownerControl)
{
}
public new UserControl1 Owner
{
get
{
return (UserControl1)base.Owner;
}
}
public override int GetChildCount()
{
return 1;
}
public override AccessibleObject GetChild(int index)
{
if (index == 0)
return new ValueAccessibleObject("MyCustomProp", Owner.MyCustomProp);
return base.GetChild(index);
}
}
}
public class ValueAccessibleObject : AccessibleObject
{
private string _name;
private string _value;
public ValueAccessibleObject(string name, string value)
{
_name = name;
_value = value;
}
public override AccessibleRole Role
{
get
{
return AccessibleRole.Text; // activate Value pattern
}
}
// note you need to override with member values, base value cannot always store something
public override string Value { get { return _value; } set { _value = value; } }
public override string Name { get { return _name; } }
}
And this is how it appears in the automation tree (using the inspect.exe tool):
Note this technique also supports writing back to the property because it's based on the ValuePattern.

MVVM: how to create a ViewModel from Model object

I want to get values of my model and create a viewmode
In my Model I have
public class TestElement
{
public TestElement CurrentNode { get; set; }
public TestElement Parent { get; set; }
}
I have some method that do this
if (thisNode == null)
{
thisNode = new TestElement { Name = name, Parent = CurrentNode };
currentCollection.Add(thisNode);
}
In my view model I want to create TestElementViewModel Parent and get my model Parent values
public class TestElementViewModel
{
public TestElementViewModel Parent { get; set; }
I want to use it in this method
public IEnumerable<TestElementViewModel> ToTreeViewModel(IEnumerable<TestElement> treemodel)
{
foreach (TestElementitem in treemodel)
yield return new TestElementViewModel
{
Id = item.Id,
Name = item.Name,
Children = ToTreeViewModel(item.Children).ToList(),
Parent = item.Parent
};
}
}
How can I achieve that?
I'm guessing your casting error occurs on the the line
Parent = item.Parent
Well the Parent property in your TestElementViewModel isn't a TestElement type so you can't do that.
Try assigning a new TestElementViewModel instead.
Parent = new TestElementViewModel { Id = item.Parent.Id, Name = item.Parent.Name, ... }
One improvement you might want to consider is using wrappers in your ViewModel class, which will make assigning properties a little easier.
For example,
public class TestElementViewModel : INotifyPropertyChanged
{
public TestElementViewModel(TestElement model)
{
Model = model;
if(Model.Parent != null)
Parent = new TestElementViewModel(Model.Parent);
}
public TestElement Model { get; private set; }
private TestElementViewModel _parent;
public TestElementViewModel Parent
{ get { return _parent; }
set { _parent = value; OnPropertyChanged("Parent"); }
}
public int Id
{
get { return Model.Id; }
set { Model.Id = value; OnPropertyChanged("Id"); }
}
// rest of the properties need wrapping too
}
makes it so that you don't have to manually assign the properties each time you instantiate a new viewmodel.

How do i detect a WPFDataGrid row Data changed?

I have a DataGrid,and need to detect when a user has make changes to a row.I don't want to use CellEditEnding because whenever a row get focus and lost it without any inputs,this event get raised,in the other way i need to bind a bool property to each row that set to true when the row got chgangd.
Use following code as an example, so you know the basic idea of how to trace if an item in your ItemSource had been changed (here only compared to the initial value only).
List<myItem> Items=new List<myItem>(); //your ItemSource
class myItem:ObservableObject //an class implement INotifyPropertyChanged interface
{
string _inititemName;
string _itemName;
bool itemChanged; //here is your indicator
myItem(string name)
{
_inititemName=itemName=name;
}
public string itemName
{
get{return _itemName;}
set
{
_itemName=vlaue;
if (_itemName!=_inititemName)
itemChanged=true;
else
itemChanged=false;
RaisePropertyChanged("itemName"); //or whatever the name of the method is that invoke OnPropertyChanged
}
}
}
Make the properties of your item class set a boolean update flag when they are modified
e.g.
public class MyGridItem
{
public MyGridItem(string Name)
{
this.Name = Name;
Updated = false;
}
public bool Updated {get; private set;}
private string _Name = null;
public string Name
{
get { return _Name; }
set {
if (!_Name.Equals( value ))
{
_Name = value;
Updated = true
}
}
}
}

How to best propagate change notifications upwards a hierarchical structure for binding?

If i have a folder-like structure that uses the composite design pattern and i bind the root folder to a TreeView. It would be quite useful if i can display certain properties that are being accumulated from the folder's contents. The question is, how do i best inform the folder that changes occurred in a child-element so that the accumulative properties get updated?
The context in which i need this is a small RSS-FeedReader i am trying to make. This are the most important objects and aspects of my model:
Composite interface:
public interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
ObservableCollection<FeedItem> FeedItems { get; }
}
FeedComposite (aka Folder)
public class FeedComposite : BindableObject, IFeedComposite
{
private string title = "";
public string Title
{
get { return title; }
set
{
title = value;
NotifyPropertyChanged("Title");
}
}
private ObservableCollection<IFeedComposite> children = new ObservableCollection<IFeedComposite>();
public ObservableCollection<IFeedComposite> Children
{
get { return children; }
set
{
children.Clear();
foreach (IFeedComposite item in value)
{
children.Add(item);
}
NotifyPropertyChanged("Children");
}
}
public FeedComposite() { }
public FeedComposite(string title)
{
Title = title;
}
public ObservableCollection<FeedItem> FeedItems
{
get
{
ObservableCollection<FeedItem> feedItems = new ObservableCollection<FeedItem>();
foreach (IFeedComposite child in Children)
{
foreach (FeedItem item in child.FeedItems)
{
feedItems.Add(item);
}
}
return feedItems;
}
}
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
Feed:
public class Feed : BindableObject, IFeedComposite
{
private string url = "";
public string Url
{
get { return url; }
set
{
url = value;
NotifyPropertyChanged("Url");
}
}
...
private ObservableCollection<FeedItem> feedItems = new ObservableCollection<FeedItem>();
public ObservableCollection<FeedItem> FeedItems
{
get { return feedItems; }
set
{
feedItems.Clear();
foreach (FeedItem item in value)
{
AddFeedItem(item);
}
NotifyPropertyChanged("Items");
}
}
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
public Feed() { }
public Feed(string url)
{
Url = url;
}
Ok, so here is the thing, if i bind a TextBlock.Text to the UnreadFeedItemsCount there won't be simple notifications when an item is marked unread, so one of my approaches has been to handle the PropertyChanged event of every FeedItem and if the IsUnread-Property is changed i have my Feed make a notification that the property UnreadFeedItemsCount has been changed. With this approach i also need to handle all PropertyChanged events of all Feeds and FeedComposites in Children of FeedComposite, from the sound of it, it should be obvious that this is not such a very good idea, you need to be very careful that items never get added to or removed from any collection without having attached the PropertyChanged event handler first.
Also: What do i do with the CollectionChanged-Events which necessarily also cause a change in the sum of the unread items count? Sounds like more event handling fun.
It is such a mess; it would be great if anyone has an elegant solution to this since i do not want the feed-reader to end up as awful as my first attempt years ago when i did not even know about DataBinding...
Well I thought I'd give your question a go, to see what I would come up with. Its untested and its is kinda along the same lines as what you already had. The major difference I made is added methods to handle the add and removal of feeds which handle the event binding needed for it to work. Theres a bit of code so here goes,
I all my code is in a single file, youll need to modify slightly if you want in in separate files.
First the groovy extension method for the PropertyChangedEventHandler
You dont need to use it, but I like it alot.
public static class NotifyPropertyChangedExtention
{
public static void Raise<T, TP>(this PropertyChangedEventHandler pc, T source, Expression<Func<T, TP>> pe)
{
if (pc != null)
{
pc.Invoke(source, new PropertyChangedEventArgs(((MemberExpression)pe.Body).Member.Name));
}
}
}
Second the FeedItem minus the feed stuff :) I have a check to raise a change event only when the value actually changes. You can see the Raise extension method in use here, no strings lovely.
class FeedItem : INotifyPropertyChanged
{
private bool _isUnread;
public event PropertyChangedEventHandler PropertyChanged;
public bool IsUnread
{
get { return _isUnread; }
set
{
if (_isUnread != value)
{
_isUnread = value;
PropertyChanged.Raise(this, x => x.IsUnread);
}
}
}
}
Now the Interfaces, I made mine differ slightly, in that my folders can contain other folders as well as feeds.
internal interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
}
internal interface IFeedFolder : IFeedComposite
{
ObservableCollection<IFeedFolder> FeedFolders { get; }
ObservableCollection<IFeed> Feeds { get; }
void AddFeed(IFeed newFeed);
void RemoveFeed(IFeed feedToRemove);
void AddFeedFolder(IFeedFolder newFeedFolder);
void RemoveFeedFolder(IFeedFolder feedFolderToRemove);
}
internal interface IFeed : IFeedComposite
{
ObservableCollection<FeedItem> FeedItems { get; }
void AddFeedItem(FeedItem newFeedItem);
void RemoveFeedItem(FeedItem feedItemToRemove);
}
Now the Feed Class, the AddFeedItem method hooks the property changed event for you, and if it is marked as unread, raises the property changed event for the count. You could overload this method, to accept a list of items, then once they have been added to the list, if any where unread, raise a single property changed event for them all.
class Feed : IFeed
{
private readonly ObservableCollection<FeedItem> _feedItems = new ObservableCollection<FeedItem>();
public event PropertyChangedEventHandler PropertyChanged;
public string Title { get; set; }
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
public ObservableCollection<FeedItem> FeedItems
{
get { return _feedItems; }
}
public void AddFeedItem(FeedItem newFeed)
{
newFeed.PropertyChanged += NewFeedPropertyChanged;
_feedItems.Add(newFeed);
PropertyChanged.Raise(this, x => x.FeedItems);
if (newFeed.IsUnread)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeedItem(FeedItem feedToRemove)
{
_feedItems.Remove(feedToRemove);
PropertyChanged.Raise(this, x => x.FeedItems);
if (feedToRemove.IsUnread)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
void NewFeedPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsUnread")
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
}
Now the FeedFolder class, much the same as the feed class but this one can hold a list of feeds and a list of feed folders (which hold their own feeds). You can easily add a method or property to return all feeditems from feeds and feedfolders if you need. Again, various checks to only raise change events if needed.
class FeedFolder : IFeedFolder
{
private readonly ObservableCollection<IFeedFolder> _feedFolders = new ObservableCollection<IFeedFolder>();
private readonly ObservableCollection<IFeed> _feeds = new ObservableCollection<IFeed>();
public event PropertyChangedEventHandler PropertyChanged;
public string Title { get; set; }
public int UnreadFeedItemsCount
{
get { return Feeds.Sum(x => x.UnreadFeedItemsCount) + FeedFolders.Sum(x => x.UnreadFeedItemsCount); }
}
public ObservableCollection<IFeedFolder> FeedFolders
{
get { return _feedFolders; }
}
public ObservableCollection<IFeed> Feeds
{
get { return _feeds; }
}
public void AddFeed(IFeed newFeed)
{
newFeed.PropertyChanged += NewFeedPropertyChanged;
_feeds.Add(newFeed);
PropertyChanged.Raise(this, x => x.Feeds);
if (newFeed.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
void NewFeedPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "UnreadFeedItemsCount")
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeed(IFeed feedToRemove)
{
_feeds.Remove(feedToRemove);
PropertyChanged.Raise(this, x => x.Feeds);
if (feedToRemove.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void AddFeedFolder(IFeedFolder newFeedFolder)
{
newFeedFolder.PropertyChanged += NewFeedPropertyChanged;
_feedFolders.Add(newFeedFolder);
PropertyChanged.Raise(this, x => x.FeedFolders);
if (newFeedFolder.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeedFolder(IFeedFolder feedFolderToRemove)
{
_feedFolders.Remove(feedFolderToRemove);
PropertyChanged.Raise(this, x => x.FeedFolders);
if (feedFolderToRemove.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
}
Now for usage, remember I havent tested this, but it should be mostly right.
var myFolder = new FeedFolder();
var myFeed = new Feed();
var myFeedItem = new FeedItem();
myFeedItem.IsUnread = true;
myFeed.AddFeedItem(myFeedItem);
myFolder.AddFeed(myFeed);
var mySecondFeedItem = new FeedItem();
//add a second feeditem to feed, but it is marked as read, so no notifications raised for unread count.
myFeed.AddFeedItem(mySecondFeedItem);
//this should fire off change events all the way up to the folder
mySecondFeedItem.IsUnread = true;

Resources