Access treeviewitem from HierarchicalDataTemplate in WPF - wpf

I am trying to access the treeviewitem created using HierarchicalDataTemplate based on the Name of the header. Also i want to access the control inside (in this case rectangle) the treeviewitem and change its color. I tried many ways but no success. Below is my code. I am generating Treeview using custom class and xml.
public partial class Window1 : Window
{
ObservableCollection<Step> TreeViewTemplate;
public Window1()
{
TreeViewTemplate = new ObservableCollection<Step>();
InitializeComponent();
SetDataTemplate("NEWSITECOPPER_PROPOSAL", "Proposal");
tvMain.ItemsSource = TreeViewTemplate;
getTreeViewItem();
}
private void getTreeViewItem()
{
TreeViewItem item = (TreeViewItem)(tvMain.ItemContainerGenerator.ContainerFromItem(tvMain.Items[3]));
}
private void SetDataTemplate(string ProcessName, string journeyName)
{
try
{
TreeViewTemplate.Clear();
//XDocument xDoc = XDocument.Load(#"C:\Users\606347769\Desktop\Hemil\Others\TreeView\TreeView\Data.xml");
XDocument xDoc = XDocument.Load(#"C:\Users\606347769\Documents\Visual Studio 2008\Projects\TestAPplication\WpfApplication1\ProcessJourneyCriteria.xml");
var JourneySteps = xDoc.Elements("ProcessAreas").Elements("Process").Where(x =>
x.Attribute("name").Value == ProcessName).Select(y =>
y.Elements("Journey").Where(k => k.Attribute("name").Value == journeyName));
var FinalSteps = JourneySteps.FirstOrDefault();
FinalSteps.Elements("Step").ToList<XElement>().ForEach(x =>
{
string key = x.Attribute("name").Value;
ObservableCollection<ChildStep> value = new ObservableCollection<ChildStep>();
x.Elements("ChildStep").ToList<XElement>().ForEach(y =>
{
ObservableCollection<GrandChildStep> GC = new ObservableCollection<GrandChildStep>();
y.Elements("GrandChildStep").ToList<XElement>().ForEach(k =>
{
GC.Add(new GrandChildStep { Name = k.Attribute("name").Value });
});
value.Add(new ChildStep { Name = y.Attribute("name").Value, GrandChildStep = GC });
});
TreeViewTemplate.Add(new Step { Name = key, ChildStep = value });
});
}
catch (Exception)
{
}
}
}
Below is the custom class i have created
class Step
{
public string Name { get; set; }
public System.Collections.ObjectModel.ObservableCollection<ChildStep> ChildStep { get; set; }
}
class ChildStep
{
public string Name { get; set; }
public System.Collections.ObjectModel.ObservableCollection<GrandChildStep> GrandChildStep { get; set; }
}
class GrandChildStep
{
public string Name { get; set; }
}

You should expose everything you want to access on your (view) model and just bind to it, like the Background of the shape or the IsSelected property of the item (needs to be bound in ItemContainerStyle).
If you need to "access UI controls" in WPF you are usually doing something wrong.

name the child,for eg. in your case x:Name="Rect" to Rectangle
then
Declare this helper method in your Code
T GetVisualChild<t>(DependencyObject parent, string name) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<t>(v, name);
}
if (child != null)
{
break;
}
}
return child;
}
then
just declare Rectangle Rect=new Rectangle(); in constructor or loaded event.
and when you want to access the child. use that declared helper method. for eg.
Rect=GetVisualChild<treeview>(this, "Rect")
Note: here "treeview" is the name of parent You may give the name of parent accessing the child directly.

Related

RaisePropertyChanged not updating properties when function called from another viewmodel

This class is used to bind search result in listbox and onselected list it will show result in overview panel, both are two different view model.
public class SearchClients : Client
{
public Client Client;
private void SelectedClient(int PartyId)
{
List<Client> c = this.fileService.FindClients(PartyId, "", "").ToList();
if (c.Count > 0)
{
Client = c[0];
}
OverviewPageViewModel viewModel = this.injector.Resolve<OverviewPageViewModel>("OverviewPage");
viewModel.SelectedClient(Client);
}
}
Search Panel View Model
public class SearchBar : BaseContentViewModel
{
private void FindClients()
{
List<Client> C = fileService.FindClients(0, SrchFirstName, SrchLastName).ToList();
}
public ICommand FindClient
{
get
{
return new RelayCommand(FindClients);
}
}
}
Result Panel View Model
public class OverviewPageViewModel: BaseContentViewModel
{
public void SelectedClient(Client Client)
{
Title = Client.TitleID;
FirstName = Client.FirstName;
LastName = Client.LastName;
}
}
ViewModel was assigned base class IContentViewModel in tab navigation, it should be initiated with OverviewPageViewModel which have all properties. Solved!!
Wrong:
IContentViewModel viewModel = injector.Resolve<IContentViewModel>(TabPage);
Correct:
IContentViewModel viewModel = injector.Resolve<IContentViewModel>(TabPage);
if (TabPage == "OverviewPage")
{
injector.Resolve<ViewModels.Windows.MainWindowViewModel>().CurrentPageViewModel = injector.Resolve<OverviewPageViewModel>(TabPage);
}
else
{
//viewModel.ClearData();
injector.Resolve<ViewModels.Windows.MainWindowViewModel>().CurrentPageViewModel = viewModel;
}

Caliburn.Micro: moving between pages with Conductor: how can I call a parent class method or property from a child class?

This is my main, parent class, ViewModel:
[AutofacRegisterType(PAGE_NAME, typeof(IMainPage), IsSingleInstance = false)]
public class MainPageViewModel : MainPageViewModelBase
{
public const string PAGE_NAME = "MainPage";
public MainPageChildsConductor ChildPages { get; private set; }
public IMainPageChild ActiveChildPage
{
get { return ChildPages.ActiveItem; }
}
public MainPageViewModel()
{
PageName = PAGE_NAME;
DisplayName = PAGE_NAME;
DisposeOnDeactivate = true;
InitChildPages();
}
private void InitChildPages()
{
ChildPages = new MainPageChildsConductor();
ChildPages.Parent = this;
ChildPages.ConductWith(this);
var trallchilds = TypeRegistry.GetItemsByType<IMainPageChild>();
var trchilds = trallchilds.Where(p => p.AutoRegister != null && p.AutoRegister.Name.StartsWith(PAGE_NAME + ":")).ToList();
var childs = new List<IMainPageChild>();
foreach (var trchild in trchilds)
{
var child = trchild.CreateType<IMainPageChild>();
childs.Add(child);
}
childs.Sort((a, b) => a.PageIndex.CompareTo(b.PageIndex));
ChildPages.Items.AddRange(childs);
ChildPages.ActivateWith(this);
ChildPages.DeactivateWith(this);
}
}
This is one of my child classes, ViewModel:
[AutofacRegisterType(PAGE_NAME, typeof(IMainPageChild), IsSingleInstance = false)]
public class Child1PageViewModel : MainPageChildViewModelBase
{
public const string PAGE_NAME = "ChildPage:Child1Page";
public const int PAGE_INDEX = 30;
public Child1PageViewModel()
{
PageName = PAGE_NAME;
DisplayName = "Child1";
PageIndex = PAGE_INDEX;
InitButtons();
InitSummaryData();
}
}
And this is the class that inherits the Caliburn.Micro class Conductor:
public class MainPageChildsConductor : Conductor<IMainPageChild>.Collection.OneActive
{
public MainPageChildsConductor()
{
}
public override void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
{
base.NotifyOfPropertyChange(propertyName);
if (Parent is INotifyPropertyChangedEx)
((INotifyPropertyChangedEx)Parent).Refresh();
}
}
The question is: how can I call a method or property that exists in the parent page 'MainPageViewModel' from the child page 'Child1PageViewModel'???
Your child view needs to inherit from Screen and when activated in the parent view model, you obtain a reference to the child VM's Parent property via inheritance from Screen.
See this page in the documentation for more details:
Screens, Conductors and Composition.
This is how I do it in one of my projects:
public class MainViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<CreateNewGraphEvent>, IHandle<AddMeasurementsToGraphEvent>, IHandle<DeleteNamedGraphEvent>,
IHandle<GraphRenamedEvent>, IHandle<AddDuplicateGraphEvent>
{
private readonly TreeListViewModel _TreeView;
private readonly StatusBarViewModel _StatusBar;
private readonly IEventAggregator _Aggregator;
private readonly ProgressDialogViewModel _Progress;
public MainViewModel(IEventAggregator aggregator, TreeListViewModel treeView, StatusBarViewModel statusBar)
{
if (aggregator == null)
throw new ArgumentNullException("aggregator");
_Aggregator = aggregator;
_Aggregator.Subscribe(this);
if (statusBar == null)
throw new ArgumentNullException("statusBar");
_StatusBar = statusBar;
if (treeView == null)
throw new ArgumentNullException("treeView");
_TreeView = treeView;
this.Items.CollectionChanged += Items_CollectionChanged;
}
public void Handle(CreateNewGraphEvent message)
{
ChartViewModel document = IoC.Get<ChartViewModel>(message.SelectedGraphType.ToString());
if (document == null) return;
document.DisplayName = message.GraphName;
document.CloseAction = this.CloseAction;
document.SelectedGraphType = message.SelectedGraphType;
ActivateItem(document);
}
}
public class ChartViewModel : Screen, IHandle<MeasurementRenamedEvent>
{
private readonly IEventAggregator _Aggregator;
private readonly ISupportServices _Services;
public ChartViewModel(IEventAggregator aggregator, ISupportServices services) : base(aggregator, services)
{
if (aggregator == null)
throw new ArgumentNullException("aggregator");
_Aggregator = aggregator;
_Aggregator.Subscribe(this);
if (services == null)
throw new ArgumentNullException("services");
_Services = services;
}}
When ActivateItem item method is called in MainViewModel, the CihldViewModel is added to the Items collection in MainViewModel and activates the child VM, where you can then access MainViewModel through the this.Parent property in ChildViewModel.

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.

BindingList<> (master) with a composed BindingList<> (child) reference

I have a situation where a BindingList<> represents a collection of POCOs that have sub-collections of similar nature, Here is a sample code of two such POCOs and their respective lists:
The DirectoryTypePoco
public class DirectoryTypePoco : IBasePoco
{
public DirectoryTypePoco()
{
}
public DirectoryTypePoco(Int16 directoryTypeId, String directoryImplementation, String directoryDescription, DirectoryDefinitionPocoList directoryDefinition)
{
DirectoryTypeId = directoryTypeId;
DirectoryImplementation = directoryImplementation;
DirectoryDescription = directoryDescription;
DirectoryDefinition = directoryDefinition;
}
public Int16 DirectoryTypeId { get; set; }
public String DirectoryImplementation { get; set; }
public String DirectoryDescription { get; set; }
public DirectoryDefinitionPocoList DirectoryDefinition { get; set; }
public object GenerateEntity(GenericRepository repository, params object[] parameters)
{
var lastMaxEntityId = repository.GetQuery<DirectoryType>().Select(select => #select.DirectoryTypeId).DefaultIfEmpty().Max();
var newEntity = new DirectoryType
{
DirectoryTypeId = (short)(lastMaxEntityId + 1),
DirectoryImplementation = this.DirectoryImplementation,
DirectoryDescription = this.DirectoryDescription
};
return newEntity;
}
}
And the BindingList<DirectoryTypePoco>:
public class DirectoryTypePocoList : BindingList<DirectoryTypePoco>
{
public DirectoryTypePocoList()
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryType>();
foreach (var r in query)
{
Add(new DirectoryTypePoco(r.DirectoryTypeId, r.DirectoryImplementation, r.DirectoryDescription, new DirectoryDefinitionPocoList(r.DirectoryTypeId)));
}
}
}
public DirectoryTypePocoList(short directoryTypeId)
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryType>(where => where.DirectoryTypeId == directoryTypeId);
foreach (var r in query)
{
Add(new DirectoryTypePoco(r.DirectoryTypeId, r.DirectoryImplementation, r.DirectoryDescription, new DirectoryDefinitionPocoList(r.DirectoryTypeId)));
}
}
}
}
The second object: DirectoryDefinitionPoco
public class DirectoryDefinitionPoco : IBasePoco
{
public DirectoryDefinitionPoco()
{
}
public DirectoryDefinitionPoco(Int16 directoryTypeId, Byte parameterId, String parameterName, String parameterValidation, Boolean encryptionRequired, PocoChangeType changeType = PocoChangeType.None)
{
DirectoryTypeId = directoryTypeId;
ParameterId = parameterId;
ParameterName = parameterName;
ParameterDescription = parameterName;
ParameterRequired = false;
ParameterValidation = parameterValidation;
EncryptionRequired = encryptionRequired;
}
public Int16 DirectoryTypeId { get; set; }
public Byte ParameterId { get; set; }
public String ParameterName { get; set; }
public String ParameterDescription { get; set; }
public String ParameterValidation { get; set; }
public Boolean ParameterRequired { get; set; }
public Boolean EncryptionRequired { get; set; }
public object GenerateEntity(GenericRepository repository, params object[] parameters)
{
var masterId = (short) parameters[0];
var lastMaxEntityId = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == masterId).Select(select => #select.ParameterId).DefaultIfEmpty().Max();
var newEntity = new DirectoryDefinition
{
DirectoryTypeId = (short)parameters[0],
ParameterId = (byte)(lastMaxEntityId + 1),
ParameterName = this.ParameterName,
ParameterDescription = this.ParameterDescription,
ParameterValidation = this.ParameterValidation,
ParameterRequired = this.ParameterRequired,
EncryptionRequired = this.EncryptionRequired
};
return newEntity;
}
}
And BindingList<DirectoryDefinitionPoco>:
public class DirectoryDefinitionPocoList : BindingList<DirectoryDefinitionPoco>
{
public DirectoryDefinitionPocoList(short directoryTypeId)
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == directoryTypeId);
foreach (var r in query)
{
Add(new DirectoryDefinitionPoco(r.DirectoryTypeId, r.ParameterId, r.ParameterName, r.ParameterValidation, r.EncryptionRequired));
}
}
}
public List<DirectoryDefinition> GetSourceQuery()
{
List<DirectoryDefinition> result;
using (var repository = new GenericRepository(new PWRDbContext()))
{
result = repository.GetQuery<DirectoryDefinition>().ToList();
}
return result;
}
public List<DirectoryDefinition> GetSourceQuery(short directoryTypeId)
{
List<DirectoryDefinition> result;
using (var repository = new GenericRepository(new PWRDbContext()))
{
result = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == directoryTypeId).ToList();
}
return result;
}
}
On the form, I load the data into the grid through a BindingSource component. The child rows are added properly and the data is valid.
Here is the issue: I'm able to add new DirectoryTypePoco but when try to add a DirectoryDefinitionPoco, in the code, the the DirectoryDefinitionPocoobject that I get has a zero for it's parent object. In the above picture, the Test5.dll234 is a DirectoryTypePoco with DirectoryTypeId = 8 and all child under it are ok except the new one I create. What am I suppose to do to make sure I have Master-Child relation in this case?
Ok. It seems that there are two thing I should have noticed in my design.
The individual child Poco needs to know the parent Poco through a reference.
The DevExpress Grid has methods that allow for retrieving the attached data to a parent row while in the child view' particular row.
The first part is straightforwards: add a new property in the child poco of parent poco type.
This however, in my case, doesn't solve my issue as when I visually add a new row on the grid, the default constructor is invoked and it takes no parameters and hence the parent poco reference will remain NULL and the Ids (numeric) will be defaulted to 0
The second point helped fix my issue completely. I was able to conjure up an extension method for the XtraGrid's GridView as follows:
public static class DevExpressGridHelper
{
public static IBasePoco GetPocoFromSelectedRow(this BaseView view)
{
return (IBasePoco)view.GetRow(((GridView)view).FocusedRowHandle);
}
public static IBasePoco GetParentPocoFromSelectedRow(this GridView view)
{
if (view.ParentView !=null)
{
// return (IBasePoco)(view.ParentView).GetRow(((GridView)(view.ParentView)).FocusedRowHandle);
return (IBasePoco)((GridView)view.ParentView).GetFocusedRow();
}
return null;
}
}
And used it as follows:
private void GridMain_Level_1_RowUpdated(object sender, RowObjectEventArgs e)
{
var view = sender as GridView;
if (view == null)
{
return;
}
var pocoObject = e.Row as DirectoryDefinitionPoco;
if (pocoObject == null)
{
return;
}
var parentPocoObject = view.GetParentPocoFromSelectedRow();
if (parentPocoObject == null)
{
return;
}
if (view.IsNewItemRow(e.RowHandle))
{
Create(pocoObject, parentPocoObject);
}
else
{
Update(pocoObject);
}
}

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.

Resources