• Using RadWindow for Popups in your MVVM application

    NOTE: A sample utilizing the code below can be found by clicking here.

    Initially I used a method I found in Prism to build a popup controller I use in my applications. However, in some cases I wanted a header or possibly allow the popup to be resized etc, etc so I decided to come up with a way to do this with the RadWindow.

    FYI, a huge thanks to Telerik support for suppying a sample that allows the Popup to adjust as the browser size adjusts.

    For now, instead of creating a project, I'm just going to post the code in this article.

    If you look through my Silverlight sections you see most of the apps I build, I use Prism because I like the Module concept. So with that, personally I create this class in my Common assembly.

    To start, create a class called RadWindowController. Note you can all it whatever you like.

    Next, paste this code in.

    Code:
     
    // Code written by Kris Frost http://www.n-stech.com
     
    publicinterfaceIRadWindowController
    {
    void CloseRadWindow();
    void ShowRadWindow(UserControl content, string headerText);
    void ShowRadWindow(UserControl content, bool isRestricted, ResizeMode resizeMode, Telerik.Windows.Controls.WindowStartupLocation windowStartUpLocation,
    string headerText);
    eventEventHandler MainRadWindowClosed;
    }
     
    [Export(typeof(IRadWindowController))]
    publicclassRadWindowController : IRadWindowController
    {
    RadWindow _radWindow;
    // Used so we can alert any consumer when the MainRadWindow has closed.
    publiceventEventHandler MainRadWindowClosed;
    public RadWindowController()
    {
     _radWindow = newRadWindow(); 
     _radWindow.Closed += newEventHandler<WindowClosedEventArgs>(RadWindow_Closed);
     SetDefaultWindowValues();
    } 
    // Call if you need to add your own command to close a popup window
    publicvoid CloseRadWindow()
    {
     _radWindow.Close();
    }
     
    public void ShowRadWindow(UserControl content, string headerText)
    {
    // There should only ever be one rad window open using this controller
    // The controller is set to Modal so a user should not be able to open
    // More than these at once anyway. If other options are needed. Create a new RadWindow in code.
    if (_radWindow.IsOpen)
      _radWindow.Close();
    // We have to wire up to the Current Host's content here so the Rad Window stays centered when the browswer is resized
     Application.Current.Host.Content.Resized += newEventHandler(Content_Resized);
     _radWindow.Content = content;
     _radWindow.Header = headerText;
     _radWindow.ShowDialog();
    }
    // Shows the view passed in a popup window
    publicvoid ShowRadWindow(UserControl content, bool isRestricted, ResizeMode resizeMode,    Telerik.Windows.Controls.WindowStartupLocation windowStartUpLocation,
    string headerText)
    {
      ShowRadWindow(content, headerText);
    }
     
    #region Private Methods
    void Content_Resized(object sender, EventArgs e)
    {
      _radWindow.Left = (Application.Current.RootVisual.RenderSize.Width / 2 - (_radWindow.ActualWidth / 2));
      _radWindow.Top = Application.Current.RootVisual.RenderSize.Height / 2 - _radWindow.ActualHeight / 2;
    }
     
    void RadWindow_Closed(object sender, WindowClosedEventArgs e)
    {
    // Set our window back to default values SetDefaultWindowValues(); // Unregister when the Window is closed. Application.Current.Host.Content.Resized -= newEventHandler(Content_Resized); // Remove whatever is assigned to the content when the window is closed. _radWindow.Content = null; if(MainRadWindowClosed != null) MainRadWindowClosed(this, null); } void SetDefaultWindowValues() { // Set Default window values to use if a caller doesn't need custom _radWindow.IsRestricted = false; _radWindow.ResizeMode = ResizeMode.NoResize; _radWindow.WindowStartupLocation = Telerik.Windows.Controls.WindowStartupLocation.CenterOwner; } #endregion
    }

    Most of this code is self explanatory. If you have any questions, Telerik has really nice documentation which you can find here.

    To cover the hightlights, I created an interface which will contain the Methods RadWindowController will implement. There is CloseRadWindow() and as the name implies it's just a call to close the RadWindow. Note, you can do this inside the UserControl you pass to one of the ShowRadWindow() methods as well.

    You can take a look here for one way of doing this.

    I have 2 ShowRadWindow methods. The 2 parameter method, takes a UserControl that wiill be assigned to the RadWindow's Content property.

    Then you have and a string which will be assigned to the RadWindows Header.

    We have a SetDefaultWindowValues() method use to set our standard settings. Do anything you like here.

    The second ShowRadWindow() is if we need the ability to change the display properties of our window. Again these should be fairly intuitive and if not, please see the Telerik documentation.

    The most important things of note are:

    1)
    // We have to wire up to the Current Host's content here so the Rad Window stays centered when the browswer is resized
    Application.Current.Host.Content.Resized += newEventHandler(Content_Resized);

    Along with:
    void Content_Resized(object sender, EventArgs e)
    {
    _radWindow.Left = (
    Application.Current.RootVisual.RenderSize.Width / 2 - (_radWindow.ActualWidth / 2));
    _radWindow.Top =
    Application.Current.RootVisual.RenderSize.Height / 2 - _radWindow.ActualHeight / 2;

    }

    Again, thanks to Telerik support for supplying this. Here we're subscribing to the Host widows Content_Resized event so we can readjust the position of the RadWindow if the user readjusts the size of the browser.

    2) In some cases we need to perform actions when a user closes a window. So in the IRadWindowController interface, I created the MainRadWindowClosed event. This allows any caller to subscribe and be notified when the RadWindow closes.

    In private code, I reset the default window values, and clear the Content and unsubscribe from the Hosts Content_Resized event.

    3) This class is made available to my modules via MEF. You'll note the [Export] attribute for the class. With this, all I have to do is use MEF's Import attribute and now I have a popupcontroller for use anywhere in my application.

    That's pretty much all there is too it. Now I know this isn't going to suit everyones needs but it should good start for anybody to change it to whatever they need.

    Since I'm not posting a fully working sample with this, I'll cover how I go about using the RadWindowController.

    As I mentioned I build most of my apps using Prism. For each module I build I create a Controller.

    The controller takes care of loading the module's Views into regions and any other initialization I need to do when the module loads.

    Also, I use a controller when I need to communicate between view models. Also, I use the controller when I need to load popups. So instead of coupling views into my ViewModels and writing code in the ViewModels to load these views into popup windows, I will create a Controller method for my ViewModels to call. The controller takes care of opening and closing the popups. You can check out the code in this lesson. The Silverlight_MVVM_Template_Northwind solution contains an implemenation of this RadWindowController.

    You can open the Northwind.Modules.Orders project and look at the OrderController.

    You will find I have an Import for the RadWindowController

    Code:
     
    [Import]
    publicIRadWindowController RadWindowController { get; set; }
    Then in the Public Methods region, you will find these two methods.

    Code:
    public void CloseMapOrderView()
    {
    RadWindowController.CloseRadWindow();
    } publicvoid ShowMapOrderView(SmartObservableCollection<OrderModel> orders) {
    if (orders == null || orders.Count < 1) MessageBox.Show("ShowMapOrderView must contain orders."); else { _mapOrderView.ViewModel.Initialize(orders); ; RadWindowController.ShowRadWindow(_mapOrderView, "Map Orders"); RadWindowController.MainRadWindowClosed += newEventHandler(OrderController_MainRadWindowClosed); }
    }
    The ShowMapOrderView() method takes in a collection of orders. If the collection contains 1 or more orders, then we pass the orders to the _mapOrderView.ViewModel.Initialize() method. This will setup the orders need when the _mapOrderView is displayed in our popup.

    We then pass the View to the RadWindowController.ShowRadWindow() and then we subscribe to the RadWindowController MainRadWindowClosed event because we want to call cleanup on the MapOrderViewModel bound to the MapOrderView.

    That ends this lesson. Again, this wasn't created to solve all scenarios but it's a good start for most I run into and I will adjust when the need arises.

    Please feel free to use this code any way you like. All I ask is you keep my name and companies URL with the code.

    Thanks and enjoy.

    Kris
    Comments 14 Comments
    1. waleed's Avatar
      waleed -
      Hello Kris,
      Impresive Example, just what I was looking for a long time...

      I am using PRISM 4, Telerik Controls, MVVM, I am still learning to get then all together.

      If you could help me getting forweard, I will be very greatfull to you..

      I just create the RadWindowController, and I am finding difficulties exploring the exampe you refered to.
      Can you give a much simpler example that show how to use RadWindow, PRISM, MVVM together.

      Again, very impressive work.
      Waleed
    1. frosty's Avatar
      frosty -
      Hello Waleed,

      First thank you for the kind comments. Next, I'm kind of pressed for time and even if I write something else it's could possibly end up only making sense to me and not others. So instead of a new example, how about we having you explain what difficulties you are having and see if we can talk through them. Hopefully it will help others as well.

      Thanks.
    1. waleed's Avatar
      waleed -
      Hello Kris,

      I guess you are right ... well I started PRISM and mvvm but I am not aware a lot about the controler concept (maybe by time).

      I have added the RadwindowController to my SL class library project.
      Then in my VM I added the the Import of the IRadwindowController, I just put the open code in my add eventhandler ...but all of sudden the main view can't be loaded at all.

      What am I missing here ...
      Best regards
      Waleed

      Sorry kris, I just moved the radwindowconroller out of the class library and now it works.
      I guess I have a problem with loading controls from class libraries using MEF.

      If you can explain more about implementing the controller would be great.
      Thanks again
    1. frosty's Avatar
      frosty -
      I picked up the concept from the Prism samples. I use Controllers to control the intereaction between objects. Mainly I use them in modules when I need to communicate between between ViewModels.

      You can use Event Agrregation but I try to events sparingly because they can get confusing.

      I.e., if I have employeeViewModel and EmployeeDetailViewModel. I may have a controller for the module that contains these VM's. It may contain a ShowEmployeeView & ShowEmployeeDetails that allow me to show the EmployeeView & EmployeeDetailView's.

      So I'm using the RadWindow to display popup windows. So I just created a IRadwindowController which defines a contract that routines through out the application can utilize for showing popups.

      I suggest you look at the Silverlight Template that contains the RadRibbon. Module1 if I recall correctly has a controller.
    1. waleed's Avatar
      waleed -
      Hi Kris,

      Why do you think, I can't get the RadWindow to open from my VM when it is located at the class library.
      I mean I should only define it at the class library and cll it from anywhere to pass my content.

      Regards
      Waleed
    1. frosty's Avatar
      frosty -
      Hello Walleed,

      I'm going to need a little more info to try and help you.

      If you are trying to use the sample I sent, the RadWindowController will implement IRadWindowController and export as type IRadWindowController.

      In the constructor of your VM or you can use a Property, you need to import the IRadWindowController.

      You should have your assemblies in your main Silverlight application. I put all of them in there. Then on any other project I add references, I set CopyLocal to false.

      Now with all this in place, in your VM, you just use one of the Methods defined in the IRadWindowController interface and the window should load.

      I suggest if you have problems, take the sample code I posted, and add a VM to that sample. Do the things I suggested above and get it to work in the sample.

      Also, take the Silverlight Template sample I have posted and use both of those to trace through and migrate the functionality into your application.
    1. waleed's Avatar
      waleed -
      Hello Kris,

      KRIS: "If you are trying to use the sample I sent, the RadWindowController will implement IRadWindowController and export as type IRadWindowController."

      WS: I have a class library "Infrastructure" where I define the IRadWindowController & the Implementer.

      In the VM If I use the constructor, The View doesn't load at all, I use it like this:
      Code:
      [ImportingConstructor]
      public SheetViewModel(IRadWindowController radWindowController, IAvtDataService dataService, IEventAggregator eventAggregator, IRegionManager regionManager)
      {
      RadWindowController = radWindowController;
      }
      If I do the import as follows:
      Code:
      [Import]
      public IRadWindowController RadWindowController { get; set; }
      The same result; the view doesn't load.

      Unless I move the Interface & the implementer to the sample project wheree I use it there.

      You mention somthing about the assimblies, do you mena the PRISM, yes I have them all in the class library : copylocal = false.

      Any help !!
      Waleed
    1. frosty's Avatar
      frosty -
      Hey Waleed,

      Sounds like you are dealing with a MEF issue. Judging by the code you posted, I would suggest you find some material on MEF and read up on it to get an understanding.

      Since you are exporting the IRadWindowController in your infrastructure, you need to make sure you initialize the composition. If you look at the samples I have, this is done in the bootstrapper to build the parts catalog. Set a breakpoint in your bootstrapper to make sure IRadWindowController is in your parts catalog after you build it. If not, keep working to get it in there.

      If it is in the parts catalog, put a break point in your RadWindowControllers constructor to make sure an exception isn't getting thrown in there.

      Also, put a break point in your app.xaml.cs file where you handle your exceptions.

      Past that, I'm not going to be able to help much more with MEF without having the code to troubleshoot myself. (I don't have time right now.)

      Again I would read the MEF documentation and post this question on MEF forums if the above doesn't get you past your problem.

      What you are dealing with is most likely something you would be running with with anything you are trying to export in your infrastructure.
    1. waleed's Avatar
      waleed -
      Hello Kris,
      I already using MEF to load xap dunamicaly and can load views & data services from different xap's.

      Could you please point-out where do you have this in your example, I can't see it

      Also, I don't know where to get the CompositionHost referece in your bootstrapper

      I don't know how to do this with class-library, that's all ..
      I will give it more try & reading.

      Best
      Waleed
    1. frosty's Avatar
      frosty -
      Waleed,

      I understand you are using MEF. But I think you could benefit from reading through documentation on it. Also, there are some good troubleshooting guides out there. The things you are asking about aren't specific any of the projects I have. They are specific to MEF & Prism.

      About all I can do here is share a section from a bootstrapper I have.

      Code:
       
              protected override CompositionContainer CreateContainer()        {            var _container = base.CreateContainer();            // Necessary so export parts from xxx get added.            this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));                        // This is necessary so the parts of the xxxget added.            
      this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(GlobalValues).Assembly));             // Initialize the container so parts are available in Modules when SatisfyImports is called.            CompositionHost.Initialize(_container);             return _container;         }
      In the code above I use the Bootstrapper type so all Exports in the main SL app get added to the AggregateCatalog.

      In my common assembly which in your case you are calling Infrastructure, I have a class called GlobalValues so I use it to initialize all the exports from that assembly.

      Then I call CompositionHost.Initialize() set a breakpoint after this and look at your parts in your AggregateCatalog.

      The IRadWindowController should be in there. I would suggest using the ImportingConstructor at least for testing.

      Make sure you aren't getting an exception in the constructor of the RadWindowController class either as that will cause you problems.

      Unfortunately that's all the time I have to spend on MEF topics right now. Again, I suggest for this issue, if what I've just typed doesn't help, check out the Silverlight forums and post under the MEF section.

      Good luck.
    1. waleed's Avatar
      waleed -
      Dear Kris,
      I appreciate your time.

      I can see the RadWindowController loaded in the AggregateCatalog, and the Iradwindow is lied under the export parts properity. But the RadWindowController constructor doesn't get hit.
      Is this also related to MEF.?

      Appreciate your time.
      Waleed
    1. frosty's Avatar
      frosty -
      A contructor for a class is standard throughout most languages. It is going to run when the class is instantiated. So yes, if you have a class with the Export attribute, when the CompositionInitializer builds the catalog, it should be instantiating an instance of any Exports it's touching. So if that's the case then the constructor has to be ran.

      As mentioned, you would best be served doing some reading about these areas before hand and get your knowledge built up first. Trying to do it while picking up on a concept such as MVVM, Prism etc is going to be extremely frustrating.

      I don't mind helping with the samples, MVVM and patterns but as I've suggested, I suggest you get the Prism 4 help chm if you are trying to build modules. Also, search and find documents that explain exactly how MEF works. Also, there are some postings out there that walk you through troubleshooting MEF problems I really suggest you read.

      Once you have read through those and have a solid understanding then you can take the samples and they will make more sense.

      Finally, your post isn't clear but it sounds like you have put the Export in an IRadWindow interface which would not be correct.

      Past this I would have to have your code to figure out what your problem is. I do consulting work on the side so if you want to explore that option, I can PM you my email address and we can establish a rate.
    1. waleed's Avatar
      waleed -
      Just want to say, I realy benifited so much from your example.
      I fixed my issue with the MEF, and now I am ready to go ...

      Thanks and best regards
      Waleed
    1. frosty's Avatar
      frosty -
      Glad to hear it. Thanks for the feedback.