Where do I add a behavior to a single Region? - prism

My problem is very simple, but all the options confuse me...
In my MEF/Prism-application, I want to attach a specific behavior to one specific region. The doumentation says, that you can do it that way:
IRegion region = regionManager.Region["Region1"];
region.Behaviors.Add("MyBehavior", new MyRegion());
But where should I put this? Is there some place, this is supposed to be done in a bootstrapper method? Currently, I am adding the behavior like this in the Loaded-event of the shell:
/// <summary>
/// Interaction logic for Shell.xaml
/// </summary>
[Export(typeof(Shell))]
public partial class Shell
{
[ImportingConstructor]
public Shell(IRegionManager regionManager, ElementViewInjectionBehavior elementViewInjectionBehavior)
{
InitializeComponent();
Loaded += (sender, args) =>
{
IRegion region = regionManager.Regions[RegionNames.ElementViewRegion];
region.Behaviors.Add("ElementViewInjection", elementViewInjectionBehavior);
};
}
}
Is this a good solution. I'd rather do it in the bootstrapper, so that it is done in the same place as the other region behavior registrations (ConfigureDefaultRegionBehaviors()).
So, the question: Where is the best place to add the behavior to one single region?

I just came up with a slightly improved solution, using a static string collection in the behavior to add the regions to attach the behavior to.
public class ViewModelInjectionBehavior : RegionBehavior, IDisposable
{
private static List<string> _regionNames;
public static List<string> Regions
{
get { return _regionNames ?? (_regionNames = new List<string>()); }
}
protected override void OnAttach()
{
if (Regions.Contains(Region.Name)) {...}
}
}
Then in my bootstrapper, I can define the regions:
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
var behaviorFactory = base.ConfigureDefaultRegionBehaviors();
ViewModelInjectionBehavior.Regions.Add(RegionNames.ElementViewRegion);
behaviorFactory.AddIfMissing("ElementViewInjectionBehavior", typeof(ViewModelInjectionBehavior));
return behaviorFactory;
}
At least, the behavior is universally usable now...

We had the same issue - in the end we just checked the region name in the region behaviour and acted only if it was that region that we wanted, kind of sucks because you are attaching the behaviour to all regions - but for us it was better than the suggested solution..
An example looks like :
public class TrackViewOpenerBehaviour : IRegionBehavior
{
public IRegion Region { get; set; }
public void Attach()
{
if (this.Region.Name == ApplicationRegions.WorkspaceRegion
|| this.Region.Name == ApplicationRegions.DialogRegion)
{
this.Region.Views.CollectionChanged += (sender, e) =>
{
//Code Here.
};
}
}
}
I always thought maybe we could create a behaviour that was responsible for attaching other behaviours to specfiic regions for us, then we could register that in the bootstrapper - but never got round to it.

Related

Dynamic ViewModel navigation

I am trying to find a way to be able to set from the View to what ViewModel I have to navigate. This is to be able to change the navigation flow without changing the core project.
I thought the easier way would be creating an interface, setting the target ViewModel there and injecting the interface into the ViewModel to then perform the navigation.
public interface IModelMapping
{
MvxViewModel ViewModelToNavigate();
}
public class MyViewModel : MvxViewModel
{
readonly IMvxNavigationService navigationService;
readonly IModelMapping modelMapping;
public MyViewModel(IMvxNavigationService navigationService, IModelMapping modelMapping)
{
this.navigationService = navigationService;
this.modelMapping = modelMapping;
}
public IMvxAsyncCommand GoContent
{
get
{
IMvxViewModel vm = modelMapping.ViewModelToNavigate();
IMvxAsyncCommand navigateCommand = new MvxAsyncCommand(() => navigationService.Navigate<vm>());
return navigteCommand;
}
}
}
The problem with this code is I am getting an error setting the navigationService.Navigate(). The error is 'vm is a variable but it is used like a type'
What about using the URI navigation together with the facade? See also https://www.mvvmcross.com/documentation/fundamentals/navigation#uri-navigation
Say you are building a task app and depending on the type of task you want to show a different view. This is where NavigationFacades come in handy (there is only so much regular expressions can do for you).
mvx://task/?id=00 <– this task is done, show read-only view (ViewModelA)
mvx://task/?id=01 <– this task isn’t, go straight to edit view (ViewModelB)
[assembly: MvxRouting(typeof(SimpleNavigationFacade), #"mvx://task/\?id=(?<id>[A-Z0-9]{32})$")]
namespace *.NavigationFacades
{
public class SimpleNavigationFacade
: IMvxNavigationFacade
{
public Task<MvxViewModelRequest> BuildViewModelRequest(string url,
IDictionary<string, string> currentParameters, MvxRequestedBy requestedBy)
{
// you can load data from a database etc.
// try not to do a lot of work here, as the user is waiting for the UI to do something ;)
var viewModelType = currentParameters["id"] == Guid.Empty.ToString("N") ? typeof(ViewModelA) : typeof(ViewModelB);
return Task.FromResult(new MvxViewModelRequest(viewModelType, new MvxBundle(), null, requestedBy));
}
}
}

Prism - moving data between viewmodels

I'm struggling to find the best implementation.
I'm using Prism and I have a View (ParentView), which has a small region within it. Depending on the item in a ddl, another smaller view (ChildView) gets injected into the region of the ParentView.
The ChildView will just have some properties which I would like to access from the ParentView.
So I realize I can use a Publish/Subscribe method to move data between viewmodels, but the issue is I have nothing to hang the Publish on. The view is made up of TextBoxes and no event triggers. The ChildView can be vastly different based on the selection of the ddl. I like the clean separation of each ChildView being it's own view injected inside the ParentView.
What is the best way to achieve this?
One solution can be to implement the interface INavigationAware in your viewmodels. After that you can use the methods onNavigatedFrom(), onNavigatedTo() and onNavigatingTo() to register your event.
EDIT:
If you want launch the event when a field in the child is changed you can do something like this:
private string _yourField;
public string YourField
{
get { return _yourField; }
set { SetProperty(ref _yourField, value);
//Here you can launch the event
}
}
In this case when YourField change the event is launched.
I tried a few implementations, but the one that worked was creating a singleton instance of the ChildView (childviewmodel) and then gaining access to the properties through the instance. It may not be pretty, but it works.
private static ChildViewModel _instance = new ChildViewModel ();
public static ChildViewModel Instance { get { return _instance; } }
#region Properties
private ChildModel _childModel= new ChildModel ();
public ChildModel _childModel
{
get { return _instance._childModel; }
set
{
SetProperty(ref _instance._childModel, value);
}
}
private string _childProperty1;
public string ChildProperty1
{
get { return _childProperty1; }
set
{
SetProperty(ref _childProperty1, value);
ChildModel.ChildProperty1= _childProperty1;
}
}
In reality - there were many childproperties. I only listed one for demo. And then I call it in ParentView
var _instance = ChildViewModel.Instance;
var _cm = _instance.ChildModel;
_parentModel = new ParentModel
{
Property1= ParentViewProperty1,
Property2= _cm.ChildProperty1,
};
Hope that helps someone else.

Prism Shell buttons shared by modules

I am using Prism 2, trying to add four navigation buttons (First Record, Last Record, Previous Record, Next Record) in shell to be used by modules. I also want these buttons to be disable if active View/ViewModel does not provide these functions.
I tried using events but didn't know how to achieve my second goal regarding disabling buttons. It seems I need to check current active View/ViewModel to see if they subscribed the click event during View switch. But I think publisher should be unaware of subscriber...
Somehow I tried my own way. I create an IDocNavigation interface which has four method corresponding to my four buttons. At runtime I check modules' ViewModel if they implemented that interface or not, and change the ICommand on fly. Below is my code. I include one LastRecordCommand only:
public ShellViewModel(Views.Shell shell)
{
this.Shell = shell;
shell.DataContext = this;
shell.MainDocking.ActivePaneChanged += (s, e) =>
{
if (e.NewPane.Content is UserControl &&
((UserControl)e.NewPane.Content).DataContext is IDocumentNavigate)
{
IDocumentNavigate vm = ((UserControl)e.NewPane.Content).DataContext as IDocumentNavigate;
LastRecordCommand = new RelayCommand(x => vm.GotoLastRecord(), x => true);
}
else
{
LastRecordCommand = new RelayCommand(x => { }, x => false);
}
};
//...
I feel these are quite ugly. Creating an empty RelayCommand is also stupid. How can I improve ? or how can I achieve disabling command if event is more suitable in my case ?
You can make use of CompositeCommand in prism.
Define a globally available CompositeCommand
public static readonly CompositeCommand FirstRecord= new CompositeCommand(true);
Then in your your module view models
class Module1
{
public DelegateCommand Module1Firstrecord{ get; set; }
Module1()
{
Module1Firstrecord = new DelegateCommand(this.FirstRecord, CanExecute);
}
private void FirstRecord()
{
//do whatever you want
}
private bool CanExecute()
{
return true;
}
private void Module1_IsActiveChanged(object sender, EventArgs e)
{
//Find if your window is acive
// if it is active Module1Firstrecord.IsActive = true
//else false.
}
}
With IActiveAware you can handle the active window scenario easily. According to whether your active module have a handler for the command on not the buttons will enable/disable.

mvvmlight - what's the "proper way" of picking up url parameters for a view model

I'm just switching a project across to mvvmlight and trying to do things "the right way"
I've got a simple app with a listbox
When an item is selected in the listbox, then I've hooked up a RelayCommand
This RelayCommand causes a call on an INavigationService (http://geekswithblogs.net/lbugnion/archive/2011/01/06/navigation-in-a-wp7-application-with-mvvm-light.aspx) which navigates to a url like "/DetailPage.xaml?DetailId=12"
The DetailPage.xaml is then loaded and ... this is where I'm a bit unsure...
how should the DetailPage get hooked up to a DetailView with DetailId of 12?
should I do this in Xaml somehow using a property on the ViewLocator?
should I do this in the NavigatedTo method?
Please feel free to point me to a full sample - sure this has been done a (hundred) thousand times before, but all the blogs and tutorials seem to be skipping this last trivial detail (focussing instead on the messaging and on the ioc on on the navigationservice)
Thanks!
The only place you can retrieve the URL parameter is in the view. So since your view is likely depending on it, you should fetch it in the OnNavigatedTo method.
Then, you should pass it along to your viewmodel, either using messaging (to expensive if you ask me), or by referring to your datacontext (which is the viewmodel I presume), and execeuting a method on that.
private AddTilePageViewModel ViewModel
{
get
{
return DataContext as AddTilePageViewModel;
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var postalCode = NavigationContext.TryGetKey("PostalCode");
var country = NavigationContext.TryGetStringKey("Country");
if (postalCode.HasValue && string.IsNullOrEmpty(country) == false)
{
ViewModel.LoadCity(postalCode.Value, country);
}
base.OnNavigatedTo(e);
}
I'm using some special extensions for the NavigationContext to make it easier.
namespace System.Windows.Navigation
{
public static class NavigationExtensions
{
public static int? TryGetKey(this NavigationContext source, string key)
{
if (source.QueryString.ContainsKey(key))
{
string value = source.QueryString[key];
int result = 0;
if (int.TryParse(value, out result))
{
return result;
}
}
return null;
}
public static string TryGetStringKey(this NavigationContext source, string key)
{
if (source.QueryString.ContainsKey(key))
{
return source.QueryString[key];
}
return null;
}
}
}
Create a new WindowsPhoneDataBound application, it has an example of how to handle navigation between views. Basically you handle the navigation part in your view, then set the view's DataContext accord to the query string. I think it plays nicely with the MVVM pattern since your ViewModels don't have to know anything about navigation (which IMO should be handled at the UI level).

What is the MVC version of this code?

i'm trying to wrap my head around how to enterprise up my code: taking a simple routine and splitting it up into 5 or 6 methods in 3 or 4 classes.
i quickly came up three simple examples of code how i currently write it. Could someone please convert these into an MVC/MVP obfuscated version?
Example 1: The last name is mandatory. Color the text box red if nothing is entered. Color it green if stuff is entered:
private void txtLastname_TextChanged(object sender, EventArgs e)
{
//Lastname mandatory.
//Color pinkish if nothing entered. Greenish if entered.
if (txtLastname.Text.Trim() == "")
{
//Lastname is required, color pinkish
txtLastname.BackColor = ControlBad;
}
else
{
//Lastname entered, remove the coloring
txtLastname.BackColor = ControlGood;
}
}
Example 2: The first name is optional, but try to get it. We'll add a bluish tint to this "try to get" field:
private void txtFirstname_TextChanged(object sender, EventArgs e)
{
//Firstname can be blank.
//Hint them that they should *try* to get it with a bluish color.
//If they do enter stuff: it better be not all spaces.
if (txtFirstname.Text == "")
{
//Nothing there, hint it blue
txtFirstname.BackColor = ControlRequired;
}
else if (txtFirstname.Text.Trim() == "")
{
//They entered spaces - bad user!
txtFirstname.BackColor = ControlBad;
}
else
{
//Entered stuff, remove coloring
txtFirstname.BackColor = SystemColors.Window;
}
}
Example 3 The age is totally optional. If an age is entered, it better be valid:
private void txtAge_TextChanged(object sender, EventArgs e)
{
//Age is optional, but if entered it better be valid
int nAge = 0;
if (Int32.TryParse(txtAge.Text, out nAge))
{
//Valid integer entered
if (nAge < 0)
{
//Negative age? i don't think so
txtAge.BackColor = ControlBad;
}
else
{
//Valid age entered, remove coloring
txtAge.BackColor = SystemColors.Window;
}
}
else
{
//Whatever is in there: it's *not* a valid integer,
if (txtAge.Text == "")
{
//Blank is okay
txtAge.BackColor = SystemColors.Window;
}
else
{
//Not a valid age, bad user
txtAge.BackColor = ControlBad;
}
}
}
Every time i see MVC code, it looks almost like random splitting of code into different methods, classes, and files. i've not been able to determine a reason or pattern to their madness. Without any understanding of they why it's being one some way, it makes no sense. And using the words model, view, controller and presenter, like i'm supposed to know what that means, doesn't help.
The model is your data.
The view shows data on screen.
The controller is used to carry out
the users actions
And oranges taste orangy.
Here's my attempt at splitting things up in order to make the code more difficult to follow. Is this anywhere close to MVC?
private void txtFirstname_TextChanged(object sender, EventArgs e)
{
FirstnameTextChangedHandler(sender, e);
}
private void FirstnameTextChangedHandler(sender, e)
{
string firstname = GetFirstname();
Color firstnameTextBoxColor = GetFirstnameTextBoxColor(firstname);
SetFirstNameTextBoxColor(firstnameTextBoxColor);
}
private string GetFirstname()
{
return txtFirstname.Text;
}
private Color GetFirstnameTextBoxColor(string firstname)
{
//Firstname can be blank.
//Hint them that they should *try* to get it with a bluish color.
//If they do enter stuff: it better be not all spaces.
if (firstname == "")
{
//Nothing there, hint it blue
return GetControlRequiredColor();
}
else if (firstname.Trim() == "")
{
//They entered spaces - bad user!
return GetControlBadColor();
}
else
{
//Entered stuff, remove coloring
return GetControlDefaultColor();
}
}
private Color GetControlRequiredColor()
{
return ControlRequired;
}
private Color GetControlBadColor()
{
return ControlBad;
}
private Color GetControlGoodColor()
{
return ControlGood;
}
//am i doin it rite
i've obfuscated the code, but it's still altogether. The next step in the MVC obfuscation, i gather, is to hide the code in 3 or 4 different files.
It's that next step that i don't understand. What is the logical separation of which functions are moved into what other classes? Can someone translate my 3 simple examples above into full fledged MVC obfuscation?
Edit: Not ASP/ASP.NET/Online. Pretend it's on a desktop, handheld, surface, kiosk. And pretend it's language agnostic.
The purpose of MVC/MVP patterns is not obfuscation, but separation of concerns. Obfuscation is to (conceal the) intended meaning in communication, making communication confusing, intentionally ambiguous, and more difficult to interpret: ref. The use of patterns is to make the code cleaner and more understandable. I suggest you start out by reading the wikipedia entries on MVC and MVP.
Both patterns are ways of structuring your code so that your application is broken up into elements that carry out specific purposes that have clearly defined interaction boundaries. Rather than having code that specifically addresses business concerns, input/output handling, and presentation throughout the various classes of the application, these concerns are separated and isolated in the various architectural components. These architectural elements are insulated from one another by the interaction boundaries (interfaces) making them more independent of one another and easier to modify without affect the application as a whole.
The main idea I have when implementing MVC for Windows Forms is that I want to have unit tests for my model and my controller. In order to achieve that, my controller should not know anything about the views using it, and so any notifications that should be handled on UI level are implemented as events. In your example, my controller would look something like this:
class Controller
{
// This is the model we are operating on
private Model model_;
public enum Status
{
Normal,
Required,
Good,
Bad
}
public delegate void FirstNameStatusChangedDelegate(Status newStatus);
public event FirstNameStatusChangedDelegate FirstNameStatusChangedEvent;
public string FirstName
{
get { return model_.FirstName; }
set
{
if (value == "")
RaiseFirstNameStatusChanged(Status.Required);
else if ( value.Trim() == "" )
RaiseFirstNameStatusChanged(Status.Bad);
else
{
model_.FirstName = value;
RaiseFirstNameStatusChanged(Status.Normal);
}
}
}
private void RaiseFirstNameStatusChanged(Status newStatus)
{
if ( FirstNameStatusChangedEvent != null )
FirstNameStatusChangedEvent(newStatus);
}
}
And the view would provide handlers for the FirstNameStatusChanged event:
class View : Form
{
private Controller controller_;
private static readonly Dictionary<Controller.Status, Color> statusColors_ = new Dictionary<Controller.Status, Color>
{
{Controller.Status.Normal, SystemColors.Window},
{Controller.Status.Required, ControlRequired},
{Controller.Status.Good, ControlGood},
{Controller.Status.Bad, ControlRed}
};
public View(Controller controller)
{
InitializeComponent();
controller_ = controller;
contoller_.FirstNameStatusChangedEvent += OnFirstNameStatusChanged;
}
private void txtFirstname_TextChanged(object sender, EventArgs e)
{ controller_.FirstName = txtFirstName.Text; }
private void OnFirstNameStatusChanged(Controller.Status newStatus)
{ txtFirstName.BackColor = statusColors_[newStatus]; }
}
Most of what you doing in your code belongs to the Controller class since it describes the the logic. Your View should just describe UI and give easy access to UI components. Model class should describe your data model.
The idea is simple: Controller does everything, but it has to know about the View and the Model. For example as View is initialized, Controller sets up all the logic ( kinda what you already doing). As Model is assigned to the Controller - it sets the values into appropriate UI controls and does the same to retrieve data and return is as Model.
So basically you give your data model class to the controller, it does the editing and returns your data as model class again.
It would be very hard to follow MVC in classic ASP.NET if possible, so I will reply based on MVP.
On your first example, you are trying to do a validation. Validating a surname is the responsibility of Presenter. Showing the field red is the responsibility of View. So, your view class would be like this:
private void Page_Load()
{
this._presenter = new Presenter();
}
private void txtLastname_TextChanged(object sender, EventArgs e)
{
txtLastName.BackColor = presenter.IsLastnameValid(txtLastName.Text) ?
ControlGood : ControlBad;
}
And your presenter class would be something like this:
public Presenter()
{
public bool IsLastNameValid(string lastname)
{
return string.IsNullOrEmpty(lastname);
}
}
Last name is your model here.
Please note that I prepared this classes only for showing how would you form an MVP structure. In real world, there are lots of better ways to do validation. Normally you would use this approach for your business instead of validation.
Ian,
If you want the controls to validate immediately, you need to use javascript or jQuery. This is also true for classic ASP.NET. Since you are using Code Behind methods, I assume that your validation waits for a postback.
The following examples are from the NerdDinner project. NerdDinner is an open source project that serves as an example of ASP.NET MVC architecture. The authors have graciously provided a tutorial with it, available at http://nerddinnerbook.s3.amazonaws.com/Intro.htm
When a form is submitted in ASP.NET MVC, it enters the corresponding controller as a FormCollection object:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
Dinner dinner = dinnerRepository.GetDinner(id);
try
{
UpdateModel(dinner);
dinnerRepository.Save();
}
catch
{
ModelState.AddModelErrors(dinner.GetRuleViolations())
}
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
UpdateModel takes the form values and attempts to stuff them into the dinner object. The dinner object looks like this:
public partial class Dinner {
public bool IsValid {
get { return (GetRuleViolations().Count() == 0); }
}
public IEnumerable<RuleViolation> GetRuleViolations() {
yield break;
}
public IEnumerable<RuleViolation> GetRuleViolations() {
if (String.IsNullOrEmpty(Title))
yield return new RuleViolation("Title is required", "Title");
if (String.IsNullOrEmpty(Description))
yield return new RuleViolation("Description is required", "Description");
if (String.IsNullOrEmpty(HostedBy))
yield return new RuleViolation("HostedBy is required", "HostedBy");
if (String.IsNullOrEmpty(Address))
yield return new RuleViolation("Address is required", "Address");
if (String.IsNullOrEmpty(Country))
yield return new RuleViolation("Country is required", "Address");
if (String.IsNullOrEmpty(ContactPhone))
yield return new RuleViolation("Phone# is required", "ContactPhone");
if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
yield return new RuleViolation("Phone# does not match country", "ContactPhone");
yield break;
}
partial void OnValidate(ChangeAction action) {
if (!IsValid)
throw new ApplicationException("Rule violations prevent saving");
}
}
Notice the IsValid method and the RuleViolations enumerator. If everything is set up properly, all you have to do is define your validations in here, and ASP.NET MVC will take care of the rest for you.
The final validated result looks like this:
I encourage you to get the NerdDinner application and tutorial at http://nerddinner.codeplex.com/

Resources