Using the ZetBox

This chapter describes common tasks and how to use the ZetBox.

Creating the UI from meta data

Properties

The default order of properties is defined by

Properties are grouped by their summary tags. Tags are separated by a comma ','. A underline '_' will be translated in a space. The order of tags is defined:

  1. Summary
  2. Main
  3. all other
  4. Meta

The tag "Hidden" is only visible to Administrators. The Tag "Main", if not available "Summary", is selected first when the object has been opened.

Tags can be translated. The translated tag is only used in the title of a property group. Thus tags should be entered in English.

Example:

Methods

Methods are not visible by default. The property "IsDisplayable" controls this.

A Method can also have category tags. Methods will be grouped in a Menu. See Properties for details.

"Show by Properties" is a way to display a Method in the Toolbar of a list property. Note: The selected item is not passed to the method.

Using custom ViewModels and Views for the UI

TODO:

Styling the UI

Styles for basic controls are defined by the zetbox. They can be overridden by registering own styles:

[Feature(NotOnFallback = true)]
public class MyWpfModule : Module
{
    protected override void Load(ContainerBuilder moduleBuilder)
    {
        base.Load(moduleBuilder);

        try
        {
            moduleBuilder
                .RegisterInstance<ResourceDictionary>(new ResourceDictionary() { Source = new Uri("/MyProject.App.Client.WPF;component/View/Common/MyProjectStyles.xaml", UriKind.Relative) })
                .WithMetadata(WPFHelper.RESOURCE_DICTIONARY_KIND, WPFHelper.RESOURCE_DICTIONARY_STYLE);
            moduleBuilder
                .RegisterInstance<ResourceDictionary>(new ResourceDictionary() { Source = new Uri("/MyProject.App.Client.WPF;component/View/Common/MyProjectViews.xaml", UriKind.Relative) })
                .WithMetadata(WPFHelper.RESOURCE_DICTIONARY_KIND, WPFHelper.RESOURCE_DICTIONARY_VIEW);
        }
        catch (Exception ex)
        {
            Logging.Client.Warn("Unable to register MyProject styles", ex);
        }
         ...
     }
}

See also: https://github.com/daszat/zetbox/tree/master/Zetbox.Client.WPF/Styles

These styles can be applied to customize the UI:

AreaGroup1 - an Area level 1

See https://github.com/daszat/zetbox/blob/master/Zetbox.Client.WPF/Styles/DefaultViews.xaml for detailed definitions

ItemGroup1 - an ItemGroup level 1

See https://github.com/daszat/zetbox/blob/master/Zetbox.Client.WPF/Styles/DefaultViews.xaml for detailed definitions

These Margins and Paddings are defined:

These Font-Sizes are defined:

To enlarge fonts global, override these styles like described earlier.

Buttons:

Other styles:

Custom code

Action classes

To implement custom actions the implementing class has to fulfil some conditions

Static action class

namespace Zetbox.App.Base
{
    [Implementor]
    public static class PropertyActions
    {
    }
}

With dependencies

namespace Zetbox.App.Base
{
    [Implementor]
    public class DataTypeActions
    {
        private static IViewModelFactory _vmf;
        private static IFrozenContext _frozenCtx;

        public DataTypeActions(IViewModelFactory vmf, IFrozenContext frozenCtx)
        {
            if (vmf == null) throw new ArgumentNullException("vmf");
            if (frozenCtx == null) throw new ArgumentNullException("frozenCtx");

            _vmf = vmf;
            _frozenCtx = frozenCtx;
        }   
    }
}

Methods

The best way to implement methods is to use the template provided by the module editor.

Examples:

[Invocation]
public static void NotifyCreated(DateRangeFilterConfiguration obj)
{
}

[Invocation]
public static void NotifyPreSave(Event obj)
{
}
        
[Invocation]
public static void NotifyDeleting(DataType obj)
{
}

[Invocation]
public static void AddProperty(DataType obj, MethodReturnEventArgs<Zetbox.App.Base.Property> e)
{
}

The UI can check, if a method can be invoked. To provide the UI with information about that fact, implement *CanExec and CanExecReason methods

[Invocation]
public static void Upload(File obj)
{
}

[Invocation]
public static void UploadCanExec(ImportedFile obj, MethodReturnEventArgs<bool> e)
{
    e.Result = obj.Blob == null;
}

[Invocation]
public static void UploadCanExecReason(ImportedFile obj, MethodReturnEventArgs<string> e)
{
    e.Result = "Changing blob on imported files is not allowed";
}

Getter/Setter

Dialogs

Sometimes it is necessary to show a Dialog. For example to select something or to edit some details.

Usage is simple, just create a SimpleDataObjectEditorTaskViewModel, pass the ViewModel (can be any view model) to edit and call ShowDialog.

var dlg = ViewModelFactory
    .CreateViewModel<SimpleDataObjectEditorTaskViewModel.Factory>()
    .Invoke(DataContext, this, item);
ViewModelFactory.ShowDialog(dlg);

If the user should chose from a list, the DataObjectSelectionTaskViewModel can be used:

var selectionTask = ViewModelFactory
    .CreateViewModel<DataObjectSelectionTaskViewModel.Factory>()
    .Invoke(
        DataContext,
        this,
        typeof(Projekt).GetObjectClass(FrozenContext),
        null,
        (objs) =>
        {
            if (objs != null)
            {
                var prj = (Projekt)objs.First().Object;
                ...
            }
        },
        null);
selectionTask.ListViewModel.AllowDelete = false;
selectionTask.ListViewModel.AllowOpen = false;
selectionTask.ListViewModel.AllowAddNew = true;
ViewModelFactory.ShowDialog(selectionTask);

The create a fully customized dialog you can use the DialogCreator

using Zetbox.Client.GUI;

var dlg = ViewModelFactory.CreateDialog(DataContext, CalendarResources.DlgDateRangeTitle)
    .AddDateTime("from", CalendarResources.FromLabel, DateTime.Today)
    .AddDateTime("to", CalendarResources.UntilLabel, DateTime.Today);
dlg.Show((values) =>
{
    var from = ((DateTime)values["from"]).Date;
    var to = ((DateTime)values["to"]).Date.AddDays(1);
    ...
});

Translations

String can be translated by the IAssetManager. The AssetManager is also provided on each ViewModel.

public class DataTypeViewModel : ... 
{
    public override string Name
    {
        get
        {
            if (_dataType.Module != null)
                return Assets.GetString(_dataType.Module, ZetboxAssetKeys.DataTypes, ZetboxAssetKeys.ConstructNameKey(_dataType), _dataType.Name);
            else
                return _dataType.Name;
        }
    }
}

[Implementor]
public class MethodActions
{
    private static IAssetsManager _assets;
    public MethodActions(IAssetsManager assets)
    {
        _assets = assets;
    }

    [Invocation]
    public static void GetLabel(Zetbox.App.Base.Method obj, MethodReturnEventArgs<System.String> e)
    {
        e.Result = !string.IsNullOrEmpty(obj.Label) ? obj.Label : obj.Name;

        if (obj.Module == null || obj.ObjectClass == null)
            return;

        e.Result = _assets.GetString(obj.Module, ZetboxAssetKeys.ConstructBaseName(obj), ZetboxAssetKeys.ConstructLabelKey(obj), e.Result);
    }
}