Create User Control dynamically

Topics: Questions
Jul 13, 2012 at 8:18 AM
Maybe I don't see the obvious, but how do I create a new User Control dynamically in code (to add it to a container) from a given view model. There is a method to show a window from a view model, but not to create a User Cotnrol. Thanks for any help, G.
Coordinator
Jul 13, 2012 at 10:12 PM

Maybe you are looking for the UIVisualizerService? If not, please explain your question with an example so I can understand.

Jul 14, 2012 at 1:17 PM

I think he looks for something like described here on stackoverflow.
By the way, it seems like the same problem talked about here

The UIVisualizerService will not work, beacuse it only creates WINDOW instances.
But here is a CONTROL instance needed which is loaded within a given content area. 

Jul 14, 2012 at 1:47 PM
Edited Jul 15, 2012 at 5:27 PM

I think you may use DataTemplate inside ContentControl binding to ValueConvert class, same like this

    <vw:VMTypeConvert x:Key="MyConverter"/>
    <DataTemplate DataType="{x:Type vm:ViewModelBase}">
        <ContentControl Content="{Binding Converter={StaticResource MyConverter}}"/>
    </DataTemplate>

 public class VMTypeConvert : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter,
            CultureInfo culture)
        {
            // value is the ViewModel. Based on its GetType(), build a string
            // with the namespace-qualified name of the view class, then:
            string viewName = "";
            string vmname = value.ToString();
            // here some codes like ViewLocator or......
            
            return Activator.CreateInstance(Type.GetType(viewName));
        }
        public object ConvertBack(object value, Type targetType,
            object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }


Jul 24, 2012 at 8:35 AM
Edited Jul 24, 2012 at 8:35 AM

Thanks alex, eric, this was exactly what I was looking for. My code looks like this:

 

public class CatelVMTypeConverter : IValueConverter
    {
        /// <summary>
        /// Creates a Catel UserControl based on the view model instance (provided by <paramref name="value"/>).
        /// </summary>
        /// <param name="value">The view model instance.</param>
        /// <param name="targetType">Not used.</param>
        /// <param name="parameter">Not used.</param>
        /// <param name="culture">Not used.</param>
        /// <returns>A Catel UserControl.</returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (WpfHelper.IsInDesignMode)
            {
                return new TextBlock { Text = string.Format("will be loaded dynamically for value «{0}»", value) };
            }
            else
            {
                var viewLocator = ServiceLocator.Instance.ResolveType<IViewLocator>();
                var viewType = viewLocator.ResolveView(value.GetType());

                EventHandler<UICompletedEventArgs> ea = new EventHandler<UICompletedEventArgs>((a, b) => { });
                var control = CatelHelper.CreateControl(viewType, value, ea);
                return control;
            }
        }

        public object ConvertBack(object value, Type targetType,
            object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }

 

 

public class CatelHelper
    {
        public static UserControl CreateControl(Type controlType, object data, EventHandler<UICompletedEventArgs> completedProc)
        {
            UserControl control = null;

            // First, try to constructor directly with the data context
            if (data != null)
            {
                ConstructorInfo constructorInfo = controlType.GetConstructor(new[] { data.GetType() });
                if (constructorInfo != null)
                {
                    control = constructorInfo.Invoke(new object[] { data }) as UserControl;
                }
            }

            // Check if there is a window constructed
            if (control == null)
            {
                ConstructorInfo constructorInfo = controlType.GetConstructor(Type.EmptyTypes);
                if (constructorInfo == null)
                {
                    //Log.Error(TraceMessages.NoInjectionOrDefaultConstructorFoundForWindow, windowType);
                    return null;
                }

                control = constructorInfo.Invoke(new object[] { }) as UserControl;

                if (control != null)
                {
                    control.DataContext = data;
                }
            }

            if ((control != null) && (completedProc != null))
            {
                control.Unloaded += (s, e) => completedProc(control, new UICompletedEventArgs(data, null));
            }

            return control;
        }
    }
#region IsInDesignMode
        // Flag indicating whether the view is loaded in the designer.
        // Access via the property getter.
        private static bool? _isInDesignMode;
        /// <summary>
        /// Gets a value indicating whether the control is in design mode
        /// (running in Blend or Visual Studio).
        /// </summary>
        public static bool IsInDesignMode
        {
            get
            {
                if (!_isInDesignMode.HasValue)
                {
                    var prop = DesignerProperties.IsInDesignModeProperty;
                    _isInDesignMode
                        = (bool)DependencyPropertyDescriptor
                        .FromProperty(prop, typeof(FrameworkElement))
                        .Metadata.DefaultValue;

                    // Just to be sure
                    if (!_isInDesignMode.Value
                        && Process.GetCurrentProcess().ProcessName.StartsWith("devenv", StringComparison.Ordinal))
                    {
                        _isInDesignMode = true;
                    }
                }

                return _isInDesignMode.Value;
            }
        }
        #endregion
<dxd:DockLayoutManager Grid.Row="1" Name="dockLayoutManager1">
                <dxd:DockLayoutManager.Resources>

                    <DataTemplate x:Key="ItemContentTemplate">
                        <ContentControl Content="{Binding Converter={StaticResource CatelVMTypeConverter}}"/>
                    </DataTemplate>
                    <DataTemplate x:Key="ItemCaptionTemplate">
                        <TextBlock Text="{Binding Title}"/>
                    </DataTemplate>
                </dxd:DockLayoutManager.Resources>
                <dxd:LayoutGroup>
                    <dxd:DocumentGroup x:Name="MainDocumentGroup" ItemsSource="{Binding OpenMasterDocuments}" 
                                           ItemContentTemplate="{StaticResource ResourceKey=ItemContentTemplate}" 
                                           ItemCaptionTemplate="{StaticResource ResourceKey=ItemCaptionTemplate}">
                        <dxd:DocumentGroup.ItemStyle>
                            <Style TargetType="dxd:DocumentPanel">
                                <Setter Property="CloseCommand" Value="{Binding CloseCommand}" />
                            </Style>
                        </dxd:DocumentGroup.ItemStyle>

                    </dxd:DocumentGroup>
                </dxd:LayoutGroup>
            </dxd:DockLayoutManager>
<catelFramework:CatelVMTypeConverter x:Key="CatelVMTypeConverter"/>