4

I want to run through a series of steps that comprise a complete test. Some of these steps are automatic (so informational) and others require user interaction. Test steps are not known at compile time, they are using MEF to be loaded.

Currently I have something like

public abstract class TestRunnerBase
{
    public abstract void Run();
}

With a list of steps like this:

 List<TestRunnerBase> Steps = new List<TestRunnerBase>();

So all data representing a test serializable and that works okay so far. However what I really need is for a user to load a test from XML, it then walks them through the options displaying information on screen and gathering results.

But trying to work out how to create a control for data that is unknown at compile time has ended up with me getting a bit stuck on the best approach.

I am thinking to do this I would have a list of custom controls (1 a step) and the GUI would display the first step, wait for that control to be complete (I was thinking here that a raised event might work?) and then display the next if available and so on until the test is complete.

So is it possible to do this in WPF? Can you create a stack of controls in WPF that can each raise the same event to the parent container or is there a better way to do it?

But if I also use the abstract class I can't then derive a control from it also as no multiple inheritance in C# of course.

Firedragon
  • 3,685
  • 3
  • 35
  • 75
  • I think it is worth mentioning that [UI is not Data](http://stackoverflow.com/questions/14381402/wpf-programming-methodology/14382137#14382137). also, there are several ways to avoid using events in general in WPF. What kind of events would you need? – Federico Berasategui Jan 29 '13 at 16:34

2 Answers2

2

I would use MVVM and create a viewmodel that understood how to navigate the list of steps, providing a wizard type structure (prev/next) and exposing the current step.

I assume that while you have different kinds of potentially unknown steps that you have a concrete set of input options (bool, text, date, int, etc) then you could use a abstract property on your TestRunnerBase that identifies what kind of input is required (or none) using an enum that must be overriden.

Then you could use datatemplates and/or data triggers to control what is shown for each step of the test. The main viewmodel could check that conditions are right for going to the next step (perhaps a validate on your test).

Some psuedo code to get you thinking:

public enum TestInput
{
    None,
    Bool,
    Text
}

public abstract class TestRunnerBase
{
    public abstract TestInput TestInput { get; }
    public bool BoolInput { get; set; }
    public string TextInput { get; set; }
    public abstract bool CanRun()
    public abstract void Run();
}

public class MainViewModel
{
    List<TestRunnerBase> Steps = new List<TestRunnerBase>();
    public TestRunnerBase CurrentStep {get;set;};

    public MainViewModel()
    {
        //loads the Steps
        CurrentStep = Steps
    }

    public Command RunStepCommand
    {
        if (CurrentStep.CanRun())
        {
            CurrentStep.Run();
            CurrentStep = Steps.Next(); //you get the idea
        }
    }
}

For your XAML you would bind a ContentPresenter to CurrentStep and use a datatemplate (and maybe data triggers) to control what is visible to the user (and of course bound to the UI).

Duane
  • 570
  • 4
  • 14
  • Thanks for this, your help is appreciated. I have had a look at `ContentPresenter` and it looks interesting but I haven't used it before. If I understand this then when `CurrentStep` changes so will the data bound to the control ad so update the control. That's clever! One thing though, the actual UI layout is dependent on the test step which is unknown at compile time (it's sort of like a plugin really so I may not write it but I can control what it needs to supply). In this case is it that the datatemplate is supplied by the test step somehow? Unfortunately it's not something I have used. – Firedragon Jan 30 '13 at 08:00
  • You could include a control in your MEF loaded assembly (along with your TestRunnerBase implementations) and add a Type property to TestRunnerBase that identified which control to load for input. might take a little work or a custom WPF control but you should be able to use that to insert the proper control into your view. – Duane Jan 30 '13 at 17:39
1

In WPF you can dynamically create controls using XAML. Simply create a XAML snippet with the layout you want (either programatically or by hand) and use XamlReader.Parse to create the entire tree from the snippet. The returned object can then be inserted somewhere in the visual tree of your window.

To generate events from the visual tree generated by XamlReader you can use routed events.

Stefan Dragnev
  • 14,143
  • 6
  • 48
  • 52