5

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;

        ...
Thomas Weller
  • 11,631
  • 3
  • 26
  • 34
  • 1
    Could you try and translate some of the German(?) because without sharing that language with you it's very hard to understand your intent and meaning. Also, does your problem happen for both CheckBoxes and TextBoxes? And what do you mean with readonly? That the IsReadOnly property is True or that your changes don't get made to the underlying ViewModel? – Zache Mar 03 '14 at 11:32
  • @Zache I edited the question to add an explanation on the German identifiers. I also tried the above with controls other than the above mentioned - always the same, they are disabled. Concerning the IsReadOnly property: This simply was a typo, of course I meant 'False'. – Thomas Weller Mar 03 '14 at 11:53
  • 1
    So it's IsEnabled=False that is your issue? – Zache Mar 03 '14 at 12:04
  • 1
    You should debug and check the value of IsEnabledCore in your Control. What you are trying to do is much better accomplished using a CustomControl rather than a UserControl, that is the way for making new and reusable controls. You should probably share the MtTemplateSelector-class also, why do you set IsHitTestVisible=False? – Zache Mar 03 '14 at 12:13
  • 1
    @Zache IsEnabled/IsEnabledCore are both 'true' - I already debugged this. Regarding the other things: I inheritedalmost all most of this code, and I wouldn't like to change it without even knowing what's going on. I added the MtTemplateSelector class to the question, but as I said it's dead simple and I'm quite sure that it's not participating in the problem. – Thomas Weller Mar 03 '14 at 12:46
  • 1
    So then the controls aren't "grayed-out"? Then IsHitTestEnalbed=False should be your problem, it prevents the controls (and their children) from receiving user input. – Zache Mar 03 '14 at 13:06
  • 1
    Tada! Removing 'IsHitTestVisible="False"' did the trick. I suspected that it's something dead simple in the end. Many thanks. If you formulate this as an answer, I will happily accept it... – Thomas Weller Mar 03 '14 at 13:18

1 Answers1

2

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.

Zache
  • 1,023
  • 6
  • 14