How to: Access Master Detail View and Nested List View Environment

This topic explains how to use Controllers to access a nested List View's or a master Detail View's environment (Frames, Controllers, Actions, Objects, etc.).

This article covers two approaches:

This topic uses the 'Contact Detail View with a Tasks nested List View' scenario. You can find the corresponding Contact and DemoTask business classes in the MainDemo application (%PUBLIC%\Documents\DevExpress Demos 18.2\Components\eXpressApp Framework\MainDemo).

AccessMasterObject2

Note

XAF creates nested List Views for collection properties. These properties are often used in Relationships Between Persistent Objects.

How to: Access a Master Detail View's Environment from a Nested List View Controller

Choose the best approach for your scenario:

If you need to access the master View from the DashboardViewItem or the DetailPropertyEditor, use the Access a Master Detail View's Frame and its Controllers approach. See the E4916 article for an example.

Access a Master Detail View's Frame and its Controllers

This approach requires two Controllers: The first is a nested List View Controller. In the code below, the NestedListViewFrameController receives the parent Frame in the AssignMasterFrame method. You can use this Frame to customize its Controllers and Actions. See Customize Controllers and Actions for more information.

Note

Nested List View Controllers cannot access the parent Detail View Frame, Controllers, and Actions unless the Detail View's Controller passes the Detail View Frame to the nested List View Controller.

public class NestedListViewFrameController : ViewController {
    private Frame masterFrame;
    public NestedListViewFrameController() {
        TargetViewType = ViewType.ListView;
        TargetViewNesting = Nesting.Nested;
    }
    public void AssignMasterFrame(Frame parentFrame) {
        masterFrame = parentFrame;
        // Use this Frame to get Controllers and Actions. 
    }
}

The second is a master Detail View Controller. The MasterDetailViewController below acquires the Detail View Frame and passes it to the NestedListViewFrameController. Use the ListPropertyEditor.FrameChanged event subscription to ensure the nested List View exists.

public class MasterDetailViewController : ViewController {
    private void PushFrameToNestedController(Frame frame) {
        foreach (Controller c in frame.Controllers) {
            if (c is NestedListViewFrameController) {
                ((NestedListViewFrameController)c).AssignMasterFrame(Frame);
            }
        }
    }
    private void lpe_FrameChanged(object sender, EventArgs e) {
        PushFrameToNestedController(((ListPropertyEditor)sender).Frame);
    }
    protected override void OnActivated() {
        base.OnActivated();
        foreach (ListPropertyEditor lpe in ((DetailView)View).GetItems<ListPropertyEditor>()) {
            if (lpe.Frame != null) {
                PushFrameToNestedController(lpe.Frame);
            }
            else {
                lpe.FrameChanged += new EventHandler<EventArgs>(lpe_FrameChanged);
            }
        }
    }
    protected override void OnDeactivated() {
        foreach (ListPropertyEditor lpe in ((DetailView)View).GetItems<ListPropertyEditor>()) {
            lpe.FrameChanged -= new EventHandler<EventArgs>(lpe_FrameChanged);
        }
        base.OnDeactivated();
    }
    public MasterDetailViewController() {
        TargetViewType = ViewType.DetailView;
    }
}
Tip

Download the E1012 example to see this approach in a sample application.

Access a Master Detail View's Current Object (the View Item Approach)

The AccessParentDetailViewController below is a nested List View Controller. The Controller uses the CurrentObjectChanged event to update the master Detail View Caption each time the CurrentObject changes.

public class AccessParentDetailViewController : ViewController {
    private void UpdateDetailViewCaption() {
        if (Frame is NestedFrame) {
            if (View.CurrentObject != null) {
                ((NestedFrame)Frame).ViewItem.View.Caption = ((DemoTask)View.CurrentObject).Subject;
            }
        }
    }
    private void View_CurrentObjectChanged(object sender, EventArgs e) {
        UpdateDetailViewCaption();
    }
    protected override void OnActivated() {
        base.OnActivated();
        View.CurrentObjectChanged += new EventHandler(View_CurrentObjectChanged);
        UpdateDetailViewCaption();
    }
    protected override void OnDeactivated() {
        base.OnDeactivated();
        View.CurrentObjectChanged -= new EventHandler(View_CurrentObjectChanged);
    }
    public AccessParentDetailViewController() {
        TargetViewType = ViewType.ListView;
        TargetViewNesting = Nesting.Nested;
        TargetObjectType = typeof(DemoTask);
    }
}
Tip

Download the E3977 example to see this approach in a sample application.

Access a Master Detail View's Current Object (the Property Collection Source Approach)

The AccessMasterObjectController below is a nested List View Controller. The nested List View's CollectionSource type is PropertyCollectionSource because the List View represents a collection property. The Controller handles the MasterObjectChanged event and processes the current MasterObject when the master object changes.

public class AccessMasterObjectController : ViewController<ListView> {
    private void UpdateMasterObject(object masterObject) {
        Contact MasterObject = (Contact)masterObject;
        // Use the master object as required.            
    }
    private void OnMasterObjectChanged(object sender, System.EventArgs e) {
        UpdateMasterObject(((PropertyCollectionSource)sender).MasterObject);
    }
    protected override void OnActivated() {
        base.OnActivated();
        PropertyCollectionSource collectionSource = View.CollectionSource as PropertyCollectionSource;
        if (collectionSource != null) {
            collectionSource.MasterObjectChanged += OnMasterObjectChanged;
            if (collectionSource.MasterObject != null){
                UpdateMasterObject(collectionSource.MasterObject);
            }
        }
    }
    protected override void OnDeactivated() {
        PropertyCollectionSource collectionSource = View.CollectionSource as PropertyCollectionSource;
        if (collectionSource != null) {
            collectionSource.MasterObjectChanged -= OnMasterObjectChanged;
        }
        base.OnDeactivated();
    }
    public AccessMasterObjectController() {
        TargetViewNesting = Nesting.Nested;
        TargetObjectType = typeof(DemoTask);
    }
}
Tip

Download the E950 example to see this approach in a sample application.

How to: Access a Nested List View's Environment from a Detail View Controller

The AccessNestedListViewController below is a Detail View Controller. The Controller handles the the CurrentObjectChanged and ControlCreated events to get the current nested List View's Frame and object. You can process them in the PerformLogicWithCurrentListViewObject and PerformLogicInNestedListViewController methods as the Customize Controllers and Actions topic describes.

Note

You can also use this approach to customize the DashboardViewItem and the DetailPropertyEditor because these View Items contain a Frame with an inner View. See the E4916 article for an example.

public class AccessNestedListViewController : ViewController {
    private void PerformLogicWithCurrentListViewObject(Object obj) {
        // Use the object in the nested List View as required.
    }
    private void PerformLogicInNestedListViewController(Frame nestedFrame) {
        // Use the nested Frame as required.
    }
    private void nestedListView_CurrentObjectChanged(object sender, EventArgs e) {
        PerformLogicWithCurrentListViewObject((DemoTask)((ListView)sender).CurrentObject);
    }
    private void listPropertyEditor_ControlCreated(object sender, EventArgs e) {
        ProcessListPropertyEditor((ListPropertyEditor)sender);
    }
    private void ProcessListPropertyEditor(ListPropertyEditor listPropertyEditor) {
        ListView nestedListView = listPropertyEditor.ListView;
        PerformLogicWithCurrentListViewObject(nestedListView.CurrentObject);
        PerformLogicInNestedListViewController(listPropertyEditor.Frame);
        nestedListView.CurrentObjectChanged += new EventHandler(nestedListView_CurrentObjectChanged);
    }
    protected override void OnActivated() {
        base.OnActivated();
        ListPropertyEditor listPropertyEditor = ((DetailView)View).FindItem("Tasks") as ListPropertyEditor;
        if (listPropertyEditor != null) {
            if (listPropertyEditor.Control != null) {
                ProcessListPropertyEditor(listPropertyEditor);
            }
            else {
                listPropertyEditor.ControlCreated += new EventHandler<EventArgs>(listPropertyEditor_ControlCreated);
            }
        }
    }
    protected override void OnDeactivated() {
        ListPropertyEditor listPropertyEditor = ((DetailView)View).FindItem("Tasks") as ListPropertyEditor;
        if (listPropertyEditor != null) {
            listPropertyEditor.ControlCreated -= new EventHandler<EventArgs>(listPropertyEditor_ControlCreated);
        }
        base.OnDeactivated();
    }
    public AccessNestedListViewController() {
        TargetViewType = ViewType.DetailView;
        TargetObjectType = typeof(Contact);
    }
}
Tip

Download the E3977 example to see this approach in a sample application.

See Also