I need to get the DataContext of the View set by using ContentSource property of the ModernWindow, Could you please help.I am using MVVM framework with Modern UI. The ViewModel code from where I need to show another window is as follows,
public void ShowPrompt()
{
this.PromptWindow = ObjectFactory.GetInstance<IPromptWindowViewModel>().Window as ModernWindow;
this.PromptWindow.Owner = Application.Current.MainWindow;
this.PWPMainViewModel.PromptWindowsCollection.Add(this.PromptWindow);
// Here I need to get the DataContext of PromptWindow's Content
this.PromptWindow.Show();
}
I did some debugging and found that by inherting IContent interface from ModernUI in the 'OnNavigatedTo' event
public void OnNavigatedTo(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e)
{
IPWPMainViewModel pwpMainViewModel = ObjectFactory.GetInstance<IPWPMainViewModel>();
pwpMainViewModel.PromptMainsCollection.Add(new ContentControl { Content = e.Content });
IPromptMainViewModel promptMainViewModel = ((UserControl)e.Content).DataContext as IPromptMainViewModel;
}
Here I am able to get the DataContext of the ModernWindow's Content i.e. of type 'IPromptMainViewModel' but here its very difficult to map/load the views into this ModernWindow as there are multiple instances of views, but I would like to do it in the ViewModel where 'ShowPrompt()' is present as there the Model will be associated with the View correctly so I can map there the views easily.
Thank you.
To get this done, I set the Content of the ModernWindow by myself (as shown in below code in a method in a ViewModel) without using the ContentSource DependencyProperty, If we use the ContentSource property it will be set for a ModernFrame type by the ModernWindow itself creating its Content instance after Navigation to that View completes in some method in ModernFrame class from ModernUI for WPF by using ModernFrame's Source DependencyProperty.
public void ShowPrompt()
{
this.PromptWindow = ObjectFactory.GetInstance<IPromptWindowViewModel>().Window as ModernWindow;
this.PromptWindow.Title = string.Concat("Control ", this.PromptOriginsEntity.PromptOriginsIdentity);
this.PromptWindow.Tag = this.PromptOriginsEntity.PromptOriginsIdentity;
this.PromptWindow.Owner = Application.Current.MainWindow;
// Store Window object in PromptWindowsCollection
this.PWPMainViewModel.PromptWindowsCollection.Add(this.PromptWindow);
this.PromptWindow.Show(); // inorder to retrieve the ModernFrame the ModernWindow is to be shown first
ModernFrame frameContent = (ModernFrame)this.PromptWindow.Template.FindName("ContentFrame", this.PromptWindow);
UserControl userControl = new UserControl { Content = GetView<IPromptViewModel>(), Tag = this.PromptOriginsEntity.PromptOriginsIdentity };
frameContent.Content = userControl;
this.PWPMainViewModel.PromptsCollection.Add(userControl);
IPromptViewModel promptViewModel = (IPromptViewModel)((IView)userControl.Content).DataContext;
promptViewModel.PromptEntity.Identity = this.PromptOriginsEntity.PromptOriginsIdentity;
}
I've uploaded a prototype app at https://wpfmvvmsamples.codeplex.com/SourceControl/latest
Thanks.
Related
I have simple ICommand-Bindings working, however I have Buttons inside an ItemsControl and wanted to get the sender information, like with the normal Routed-Events (object sender, e RoutedEventArgs) and this seems not to be possible with the normal ICommands, right?
I am a little bit lost here.
I currently use the Prism 6 DelegateCommand-Class to get things working. It looks like this:
private ICommand _selectCommand;
public ICommand SelectCommand
{
get
{
return _selectCommand ?? (_selectCommand = new DelegateCommand<object>(SelectImage));
}
}
private void SelectImage(object image)
{
var img = (BitmapImage)image;
var index = Scans.IndexOf(img);
this.CurrentIndex = index + 1;
ImageToDisplay = img;
}
How I can I get the RoutedCommand to work?
A view model is not supposed to be accessing or even know about any view element.
You should bind a target property of the control in the view to a source property of the view model that you can simply set in your SelectImage method when your command gets executed.
I have a user control with a button which when clicked opens a new user control.
private void Button_Click(object sender, RoutedEventArgs e)
{
Window window = new Window
{
Title = "Window2",
Content = new UserDataControl2()
};
window.ShowDialog();
}
I need to pass a collection to the new user control. How can I do it?
The easiest way is to create a custom constructor for your user control.
// Button_Click event
Window window = new Window
{
Title = "Window2",
Content = new UserDataControl2("My Data");
};
// User Control class.
string _info;
public UserDataControl2(string info)
{
_info = info.
};
You could also create a method or property in the user control to receive the data as well. Use whichever seems more appropriate in your context.
The best way is passing object to DataContext of this Window. For this you will need to create a class where store this parameters (ViewModels) and after "binding" to the Window (View). After you can pass this object assigning to Datacontext.
Look to MVVM model to understand better what I mean.
MVVM Pattern Made Simple
MVVM in Depth
I have a created a window ( WPF and MVVM ) - say PrintWidow ( so I have PrintWindow.xaml , PrintWindow.xaml.cs , PrintWindowViewModel.cs- viewmodel)
Now I am going to use(call) this PrintWindow obj from some other class on button click or on some command trigger , I want to set Document Source for this PrintWindow(following MVVM).
How would I do this ? I created a PrintDocument object in PrintWindow.xaml.cs and tried to bind it as follows : (obviously just a blank try - as I cannot do this declaration in XAML)
private PrintDocument printDocuementView;
public PrintDocument PrintDocuement
{
get { return printDocuementView; }
set { printDocuementView = value; }
}
//constructor
public PrintWindow()
{
InitializeComponent();
this.DataContext = new PrintViewModel();
Binding b = new Binding();
b.Source = printDocuementView;
b.Path = new PropertyPath("PrintDocumentCommand"); // "PrintDocumentCommand" is defined in View Model class and is responsible to set the `PrintDocument` object there.
}
This code (obviously) doesn't work. How should I go about it.
Summary : I want to open PrintWindow from another window and eventually set some Property of PrintWindow from code behind of the 'other widow' object.The query is - where should this property go? View ? ViewModel? ?? puzzzeled
I have googled for answers - but couldn't relate any to my problem.
I am a Freshman for WPF and a Rookie for MVVM.
Since your PrintDocumentCommand is in your PrintViewModel but you're setting the Source of this Binding to an instance of the PrintDocument-Class, it can't be found, because the Binding is looking for the PrintDocumentCommand in PrintDocument-Class.
If you want to open the PrintWindow from another Window, place the PrintDocument-Property and the PrintDocumentCommand in the ViewModel of the other Window. Now your function that is executed through the PrintDocumentCommand could look like:
private void Print()
{
PrintWindow pw = new PrintWindow(PrintDocument);
pw.ShowDialog();
}
The constructor of your PrintView could be like:
public PrintWindow(PrintDocument pd)
{
InitializeComponents();
this.DataContext = new PrintViewModel(pd);
}
and now you can access the PrintDocument in your PrintViewModel.
I've a child view (SharedView) that's shared between two parent views so I add it to each parent view through a Region like this
<StackPanel>
<ContentControl cal:RegionManager.RegionName="SharedViewRegion" />
</StackPanel>
In the parent view's ViewModel I register the child view like this
regionManager.RegisterViewWithRegion("SharedViewRegion", typeof(SharedView));
When I run the application if I open only one of the parent views it works as expected but if I open the two parent views then I get the following exception
An exception occurred while creating a region with name
'SecondRegion'. The exception was: System.InvalidOperationException:
Specified element is already the logical child of another element.
Disconnect it first.
I've been googling and this is the closer solution I found to my problem InvalidOperationException occurs when the same view instance is added to multiple ContentControl regions
But I'm using the prism navigation feature so I'm instancing the parent view like this
regionManager.RequestNavigate("ModuleRegion", new Uri("ParentView1", UriKind.Relative));
Can someone help me to solve this?
Try doing the following:
Add a name to the ContentControl that is hosting the region
Now you have to remove the region's content before leaving the parent view so in the ViewModel add the following code to the OnNavigatedFrom method
public void OnNavigatedFrom(NavigationContext navigationContext)
{
ParentView.MyContentControl.Content = null;
}
Note: You can access the parent view importing it in your ViewModel.
Now you need to add the content to the region by hand because you removed it before leaving the region. Here's the code
public void OnNavigatedTo(NavigationContext navigationContext)
{
SharedView view = (SharedView)ServiceLocator.Current.GetInstance(typeof(SharedView));
ParentView.MyContentControl.Content = view;
}
Note: In this method you must add some workaround because the first time you open this view you'll get a System.InvalidOperationException because PRISM will try to add the SharedView to MyContentControl.
Here's a possible workaround
bool isFirstTime = true;
public void OnNavigatedTo(NavigationContext navigationContext)
{
if (isFirstTime)
{
isFirstTime = false;
return;
}
SharedView view = (SharedView)ServiceLocator.Current.GetInstance(typeof(SharedView));
ParentView.MyContentControl.Content = view;
}
You have to do the same work in all parent views that share the SharedView
I hope this help you
You should directly add view to region instead of view discovery for this case. Give unique names to your parent views (you can use a counter to make up a unique name).
var region = this.regionManager.Regions["SharedViewRegion"];
var viewName = string.Format("SharedView{0}", this.countParent+1);
//Only add if it doesn't exist
var view = region.GetView(viewName);
if (null==view)
{
//Use container to get new instance of the parent view.
view = this.container.Resolve<SharedView>();
region.Add(view, name);
//Increment the counter as you added a new parent view
this.countParent++;
}
//Now activate the view
region.Activate(view);
I'm new to WPF and the MVVM pattern so keep that in mind.
The project I'm tasked with working on has a view and a view model. The view also contains a user control that does NOT have a view model. There is data (custom object ... Order) being passed to the view model that I also need to share with the user control.
It looks like the UserControl does share data between the view model already via DependencyPropertys but this data is just text boxes on the user control that look to be bound back to propertys on the view model.
I need to share data that will NOT be represented by a control on the user control. Is there a good way to pass this data (complex Order object)? Maybe I do need some kind of hidden control on my user control to accomplish this but I'm just not that sure being new to this. Any advice would be appreciated.
There is no need for hidden fields (or any such concept in WPF) as you can add any custom properties you want to a user control.
In the user control, create a new dependancy property like this but with MyUserControl set apropriately:
public Order CurrentOrder
{
get { return (Order)GetValue(CurrentOrderProperty); }
set { SetValue(CurrentOrderProperty, value); }
}
// Using a DependencyProperty as the backing store for CurrentOrder. This enables binding, etc.
public static readonly DependencyProperty CurrentOrderProperty =
DependencyProperty.Register("CurrentOrder", typeof(Order), typeof(MyUserControl), new PropertyMetadata(null, OnCurrentOrderPropertyChanged));
public static void OnCurrentOrderPropertyChanged(DependencyObject Sender, DependencyPropertyChangedEventArgs e)
{
var sender = Sender as MyUserControl;
var NewValue = e.NewValue as Order;
var OldValue = e.OldValue as Order;
if (OldValue != null && sender != null)
{
//Use old value as needed and use sender instead of this as method is static.
}
if (NewValue != null && sender != null)
{
//Use new value as needed and use sender instead of this as method is static.
}
}
In you're parent view where you use the usercontrol you then write something like:
<local:MyUserControl CurrentOrder="{Binding ViewModelOrder}" />
Where CurrentOrder is the dependancy property on the usercontrol and ViewModelOrder is the name of the property in the view model you would need to replace local:MyUserControl with your control name/namespace.
You can simply create a dependency property in the class of your UserControl and bind to it in the View that uses the control. There is no need to internally bind the dependency property to one of the controls in the UserControl.