RaisePropertyChanged not updating properties when function called from another viewmodel - wpf

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;
}

Related

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.

Access treeviewitem from HierarchicalDataTemplate in 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.

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.

Injecting Commands into ViewModels

I'm trying to figure out what is the right way to inject an ICommand into my ViewModel.
Given that my ViewModel looks like this.
public class ViewModel : IViewModel
{
ICommand LoadCommand { get; }
ICommand SaveCommand { get; }
}
I currently do this in my constructor
public ViewModel(IRepository repository, IErrorLog errorLog, IValidator validator)
{
LoadCommand = new LoadCommandImpl(repository, errorLog);
SaveCommand = new SaveCommandImpl(repository, errorLog, validator);
}
Note that the parameters are not used by the ViewModel at all, aside from constructing the commands.
While I try to contain as much of the logic as possible in the injected interfaces, there is still logic in the commands.
It would seem more appropriate to do this
public ViewModel(ICommand loadCommand, ICommand saveCommand)
{
LoadCommand = loadCommand;
SaveCommand = saveCommand;
LoadCommand.SetViewModel(this);
SaveCommand.SetViewModel(this);
}
However to do this, I would need to make my Unity registrations like this. Which isn't the end of the world, but it seems like a pain.
container.RegisterType<ICommand, LoadCommandImpl>("loadCommand");
container.RegisterType<ICommand, SaveCommandImpl>("saveCommand");
container.RegisterType<IViewModel, ViewModel>(
new InjectionConstructor(
new ResolvedParameter<ICommand>("loadCommand"),
new ResolvedParameter<ICommand>("SaveCommand")));
Alternatively, I could make ILoadCommand and ISaveCommand interfaces, but these interfaces would be empty or might implement ICommand.
I'm not a huge fan of any of these solutions. What is the recommended approach here?
Edit in response to blindmeis
Let's pretend this is something other than commands for a moment.
public ViewModel(IFoo foo)
{
Bar = new Bar(foo);
}
In my opinion, it would be more appropriate to just inject IBar
public ViewModel(IBar bar)
{
Bar = bar;
}
But now I have Bar1 and Bar2. So I can either do
public ViewModel(IFoo foo)
{
Bar1 = new Bar1(foo);
Bar2 = new Bar2(foo);
}
or
public ViewModel(IBar bar1, IBar bar2)
{
Bar1 = bar1;
Bar2 = bar2;
}
This behavior is not included in Unity but its not hard to retrofit.
var container = new UnityContainer();
container.AddNewExtension<MapParameterNamesToRegistrationNamesExtension>();
container.RegisterType<ICommand, LoadCommand>("loadCommand");
container.RegisterType<ICommand, SaveCommand>("saveCommand");
container.RegisterType<ViewModel>(new MapParameterNameToRegistrationName());
var vm = container.Resolve<ViewModel>();
Assert.IsType(typeof(LoadCommand), vm.LoadCommand);
Assert.IsType(typeof(SaveCommand), vm.SaveCommand);
public class MapParameterNamesToRegistrationNamesExtension : UnityContainerExtension
{
protected override void Initialize()
{
var strategy = new MapParameterNamesToRegistrationNamesStrategy();
this.Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}
}
public class MapParameterNamesToRegistrationNamesStrategy : BuilderStrategy
{
public override void PreBuildUp(IBuilderContext context)
{
if (context.Policies.Get<IMapParameterNameToRegistrationNamePolicy>(context.BuildKey) == null)
{
return;
}
IPolicyList resolverPolicyDestination;
IConstructorSelectorPolicy selector = context.Policies.Get<IConstructorSelectorPolicy>(context.BuildKey, out resolverPolicyDestination);
var selectedConstructor = selector.SelectConstructor(context, resolverPolicyDestination);
if (selectedConstructor == null)
{
return;
}
var parameters = selectedConstructor.Constructor.GetParameters();
var parameterKeys = selectedConstructor.GetParameterKeys();
for (int i = 0; i < parameters.Length; i++)
{
Type parameterType = parameters[i].ParameterType;
if (parameterType.IsAbstract || parameterType.IsInterface)
{
IDependencyResolverPolicy resolverPolicy = new NamedTypeDependencyResolverPolicy(parameterType, parameters[i].Name);
context.Policies.Set<IDependencyResolverPolicy>(resolverPolicy, parameterKeys[i]);
}
}
resolverPolicyDestination.Set<IConstructorSelectorPolicy>(new SelectedConstructorCache(selectedConstructor), context.BuildKey);
}
}
public class MapParameterNameToRegistrationName : InjectionMember
{
public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
{
policies.Set<IMapParameterNameToRegistrationNamePolicy>(new MapParameterNameToRegistrationNamePolicy(), new NamedTypeBuildKey(implementationType, name));
}
}
public interface IMapParameterNameToRegistrationNamePolicy : IBuilderPolicy
{
}
public class MapParameterNameToRegistrationNamePolicy : IMapParameterNameToRegistrationNamePolicy
{
}
The code and test can be found in the source code of the TecX project on CodePlex. Project TecX.Unity (folder Injection).
Why dont you create a command Factory
public class CommandFactory (IUnityContainer container) : ICommandFactory
{
public ICommand CreateSaveCommand()
{
return container.Resolve("SaveCommand");
}
public ICommand CreateLoadCommand()
{
return container.Resolve("LoadCommand");
}
}
public ViewModel(ICommandFactory commandFactory)
{
LoadCommand = commandFactory.CreateLoadCommand();
SaveCommand = commandFactory.CreateSaveCommand();
}

Resources