Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my custom control always read-only?

I'm working on a LightSwitch custom control that is able (well, at least this is intended...) to bind to and edit various different properties, based on a discriminating value.

Let me explain a bit further. The table looks like this: enter image description here

Now, based on the value of Datenformat, the control binds itself dynamically to ErgebnisBool, ErgebnisDatum and so on and selects the appropriate DataTemplate from the control's xaml. ErgebnisAnzeige is a computed text property that holds a read-only display string.

The control's xaml is as follows:

<UserControl x:Class="EuvControlsExtension.Presentation.Controls.ProzessErgebnisEdit"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:framework ="clr-namespace:Microsoft.LightSwitch.Presentation.Framework;assembly=Microsoft.LightSwitch.Client"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:ct="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input"
    xmlns:multi="clr-namespace:EuvControlsExtension.Presentation.Controls"
    xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    mc:Ignorable="d"
    x:Name="HostControl">

  <multi:MtTemplateSelector x:Name="MyTemplateSelector" 
                            Content="{Binding Path=RootEntity, Mode=TwoWay, ElementName=HostControl, UpdateSourceTrigger=Default}" 
                            HorizontalContentAlignment="Stretch" 
                            VerticalContentAlignment="Center"
                            IsHitTestVisible="False">

        <!--Zahl-->
        <multi:MtTemplateSelector.ZahlTemplate>
            <DataTemplate>
                <TextBox HorizontalAlignment="Stretch"
                 Text="{Binding Path=RootEntity.ErgebnisZahl, ElementName=HostControl, Mode=TwoWay, UpdateSourceTrigger=Default, ValidatesOnExceptions=True, NotifyOnValidationError=True}"/>
            </DataTemplate>
        </multi:MtTemplateSelector.ZahlTemplate>

        <!--Bool-->
        <multi:MtTemplateSelector.BoolTemplate>
            <DataTemplate>
                <CheckBox IsThreeState="True"
                 Content="{Binding Path=RootEntity.ErgebnisBool, ElementName=HostControl, Mode=TwoWay, UpdateSourceTrigger=Default, ValidatesOnExceptions=True, NotifyOnValidationError=True}"/>
            </DataTemplate>
        </multi:MtTemplateSelector.BoolTemplate>

    </multi:MtTemplateSelector>
</UserControl> 

The MtTemplateSelector class simply returns the appropriate DataTemplate for the current format, nothing special here.

The control itself (ProzessErgebnisEdit) is also very simple. It does nothing than binding the entity instance (called RootEntity):

public partial class ProzessErgebnisEdit : UserControl
{
    public ProzessErgebnisEdit()
    {
        InitializeComponent();
        BindProperties();
    }

    private void BindProperties()
    {
        var binding = new Binding("DataSourceRoot.RootObject")
                          {
                              Mode = BindingMode.TwoWay
                          };
        this.SetBinding(RootEntityProperty, binding);        
    }

    public object RootEntity
    {
        get { return GetValue(RootEntityProperty); }
        set { SetValue(RootEntityProperty, value); }
    }

    public static readonly DependencyProperty RootEntityProperty = DependencyProperty.Register(
        "RootEntity", 
        typeof(object), 
        typeof(ProzessErgebnisEdit), 
        new PropertyMetadata(null));
}

Last not least I have this control factory that returns a display-only DataTemplate, when appropriate:

[Export(typeof(IControlFactory))]
[ControlFactory("EuvControlsExtension:ProzessErgebnisEdit")]
internal class ProzessErgebnisEditFactory : IControlFactory
{
    private DataTemplate dataTemplate;
    private DataTemplate displayModeDataTemplate;

    public DataTemplate DataTemplate
    {
        get {
            return this.dataTemplate ??
                   (this.dataTemplate = XamlReader.Load(ControlTemplate) as DataTemplate);
        }
    }

    public DataTemplate GetDisplayModeDataTemplate(IContentItem contentItem)
    {
        return this.displayModeDataTemplate ??
               (this.displayModeDataTemplate = XamlReader.Load(DisplayModeControlTemplate) as DataTemplate);
    }

    private const string DisplayModeControlTemplate =
        "<DataTemplate" +
        "  xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
        "  <Border Background='GreenYellow'>" +
        "     <TextBlock Text='{Binding Path=DataSourceRoot.RootObject.ErgebnisAnzeige}' Margin='3' Foreground='Red' " +
        "             TextAlignment=\"{Binding Properties[Microsoft.LightSwitch:RootControl/TextAlignment]}\"" +
        "             VerticalAlignment=\"{Binding Properties[Microsoft.LightSwitch:RootControl/VerticalAlignment]}\" />" +
        "  </Border>" +
        "</DataTemplate>";

    private const string ControlTemplate =
        "<DataTemplate" +
        " xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"" +
        " xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"" +
        " xmlns:ctl=\"clr-namespace:EuvControlsExtension.Presentation.Controls;assembly=EuvControlsExtension.Client\">" +
        "<ctl:ProzessErgebnisEdit/>" +
        "</DataTemplate>";
}

Everything works fine so far: When the cell is not focused, the display-only text appears (e.g. Wahr/Falsch/--), and when I click to edit a cell, it switches to the approriate DataTemplate (e.g. a Checkbox). And there is always the correct value displayed, which tells me that the databinding generally works.

The only problem is that the controls are always read-only, and I'm not able to make them editable via code or xaml or whatever.

So far I tried to intercept the grid's OnPrepareCellEditing event and then setting the IsReadOnly property of the respective column manually to false, and I also tried the same with the control's DataContext.IsReadOnly property. No luck...

What am I missing? Is my entire approach flawed? What's going on here? I can't figure out a reason for this behavior, I don't even see a direction for searching...

Edit: On the german identifiers Some of the identifiers are in German und thus might not be immediately clear to everyone. But in the end, it's as simple as this: Ergebnis is German for Result. This expression appears frequently in the table because the value to display is the result of a physical production process which can be of various data types such as bool, string, datetime etc. (nothing fancy here). This information is the content of the Datenformat column, which would be something like DataType in English.

Edit2: The template selector:

public class MtTemplateSelector : DataTemplateSelector
{
    public DataTemplate UnknownTemplate { get; set; }
    public DataTemplate ZahlTemplate { get; set; }
    public DataTemplate FreitextTemplate { get; set; }
    public DataTemplate AuswahlTemplate { get; set; }
    public DataTemplate BoolTemplate { get; set; }
    public DataTemplate DatumTemplate { get; set; }
    public DataTemplate MessungTemplate { get; set; }
    public DataTemplate DateiPfadTemplate { get; set; }
    public DataTemplate OrdnerPfadTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        switch (GetDatenFormatEnum(item))
        {
            case DatenformatEnum.Zahl:
                return ZahlTemplate;

        ...
like image 355
Thomas Weller Avatar asked Nov 02 '22 04:11

Thomas Weller


1 Answers

You simply need to remove the IsHitTestVisile=False from the multi:MtTemplateSelector this prevents it, and all it's children, from getting events from user interactions. You can learn more here.

like image 186
Zache Avatar answered Nov 15 '22 06:11

Zache