5

In Silverlight 4, the IsReadOnly property of DataGridTextColumn seems to be no dependency property. Hence I could not bind it to a property on the viewmodel.

It seems the only alternative is using a DataTemplate, but even here I am facing two major problems:

<sdk:DataGrid Style="{StaticResource DataGridStyle}" x:Name="call_dataGrid" ItemsSource="{Binding Calls}">
                    <sdk:DataGrid.Columns>
                        <sdk:DataGridTextColumn Header="Call Time" Binding="{Binding Path=CallTime}" />
                        <sdk:DataGridTemplateColumn Header="Call Date">
                            <sdk:DataGridTemplateColumn.CellEditingTemplate>
                                <DataTemplate>
                                    <TextBox Text="{Binding Path=CallDate}" IsReadOnly="{Binding Path=DataContext.IsInEditMode, ElementName=call_dataGrid, Converter={StaticResource NegationConverter}}"/>
                                </DataTemplate>
                            </sdk:DataGridTemplateColumn.CellEditingTemplate>
                        </sdk:DataGridTemplateColumn>

It seems I can't edit a template of DataGridTextColumn and have to use DataGridTemplateColumn instead as seen above. This however overrides all the styles I had previously defined within the DataGridStyle. My Column doesn't even have the row marker and looks totally alien to the rest of the cells.

Second problem is, it still doesn't work as intended. The Textbox inside that template is still not set to readonly. What I am doing wrong here?

Highly appreciate your help on this,

Update

After the promising response below I have adjusted the code yet no success.

I have changed the DP's callback to the following

public class IsReadOnlyDpAttachable
    {
        public static void SetIsReadXOnly(DependencyObject obj, bool isReadOnly)
        {
            obj.SetValue(IsReadXOnlyProperty, isReadOnly);
        }

        public static bool GetIsReadXOnly(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsReadXOnlyProperty);
        }

        public static readonly DependencyProperty IsReadXOnlyProperty =
            DependencyProperty.RegisterAttached("IsReadXOnly", typeof(bool), typeof(IsReadOnlyDpAttachable), new PropertyMetadata(false, Callback));

        private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DataGrid)d).IsReadOnly = (bool)e.NewValue;
        }
    }

And set the DP on the DataGrid's IsReadOnly itself, it works perfectly fine, but then again here I wouldn't need it since the IsReadOnly here is already a Dp and can be easily bound anyway. But the test shows the Dp works fine:

<sdk:DataGrid PrismExt:IsReadOnlyDpAttachable.IsReadXOnly="{Binding IsInEditMode, Mode=TwoWay, Converter={StaticResource NegationConverter}}" Style="{StaticResource DataGridStyle}" CanUserReorderColumns="True" x:Name="call_dataGrid" AutoGenerateColumns="False" ItemsSource="{Binding Calls}">

However the moment I try to utilize the DP on the underlying DataGridTextColumn, it doesn't do anything:

<Grid x:Name="LayoutRoot">
<sdk:DataGrid Style="{StaticResource DataGridStyle}" CanUserReorderColumns="True" x:Name="call_dataGrid" AutoGenerateColumns="False" ItemsSource="{Binding Calls}">
                    <sdk:DataGrid.Columns>                        
                        <sdk:DataGridTextColumn Header="Call Time" Binding="{Binding Path=CallTime}" PrismExt:IsReadOnlyDpAttachable.IsReadXOnly="{Binding DataContext.IsInEditMode, ElementName=LayoutRoot, Mode=TwoWay, Converter={StaticResource NegationConverter}}"/>
                    </sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>

Any idea?

Houman
  • 64,245
  • 87
  • 278
  • 460

1 Answers1

7

what you can do is to create an attached property to handle the change of the IsReadOnly property in the DataGridTextColumn.

public class Class1
{
    public static void SetIsReadOnly(DependencyObject obj, bool isReadOnly)
    {
        obj.SetValue(IsReadOnlyProperty, isReadOnly);
    }

    public static bool GetIsReadOnly(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsReadOnlyProperty);
    }

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsReadOnlyProperty =
        DependencyProperty.RegisterAttached("IsReadOnly", typeof(bool), typeof(Class1), new PropertyMetadata(false, Callback));

    private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((DataGridTextColumn)d).IsReadOnly = (bool)e.NewValue;
    }
}

In your xaml, you can just use the attached property instead.

<sdk:DataGridTextColumn local:Class1.IsReadOnly="True" Binding="{Binding Property1}" Header="Property1"/>

Hope this helps. :)

Update

Once you have the DataContextProxy class, you do

        <sdk:DataGridTextColumn Binding="{Binding Name}"
                                local:Class1.IsReadOnly="{Binding DataSource.IsInEditMode, Source={StaticResource DataContextProxy}, Converter={StaticResource xxxConverter}}"
                                Header="ReadOnly Header" />
Justin XL
  • 38,763
  • 7
  • 88
  • 133
  • Many Thanks for your response. However while a normal true or false works, I still can't utilize the binding like this: – Houman May 10 '11 at 07:08
  • can you put a breakput in your NegationConverter and see if it's called? – Justin XL May 10 '11 at 07:14
  • I did as you said, but no the converted is not stopping at the Convert method. If I use It works. So the underlying property 'IsInEditMode' is fine. I don't understand why its not working. I thought thats the whole point of a dependency property to allow bindings – Houman May 10 '11 at 09:13
  • 1
    okay i see what the problem is. i assume your property IsInEditMode is in the data context of your DataGrid but not the data context of your DataGridTextColumn. so you need to name your very top element on your page. let's say it is a Grid, called 'LayoutRoot', then you need to use ElementBinding to get the parent DataContext, something like this, – Justin XL May 10 '11 at 12:07
  • You are right Xin, this should be the correct approach. I only had the simplified version here for sake of writing less code. But you are right. However it still doesn't work and I don't understand why. Did this example actually work for you? I might be doing a tiny bit wrong, that is not easily visible.. :( darn... – Houman May 10 '11 at 13:01
  • Xin, does the Class1 has to inherit from anything? It is so weird. If I doubleclick in the DataGridTextColumn cell, I get into edit mode, which I shouldn't be. Then I click into cell next to it and then click back, and only then the breakpoint at IsInEditMode is hit at all. But the converter is nonetheless ignored. I Don't understand. Something is not right here and its driving me crazy.. – Houman May 11 '11 at 06:57
  • can you show me the implementation of your DataGridTextColumn? the Class1 doesn't inherit from anything. – Justin XL May 11 '11 at 11:47
  • Xin, I have updated my answer, please check it out and tell me what you think. Many Thanks – Houman May 11 '11 at 19:24
  • 1
    Kave, my apologize, the ElementName binding acutally won't work inside a datagrid cell. You need to take a look at this post to include a DataContextProxy. http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx I will update my answer in a sec. – Justin XL May 12 '11 at 00:34
  • 1
    I have also updated a sample project here just in case. http://cid-f9ac8aa3f6afdcc3.office.live.com/self.aspx/Public/datagridreadonly.zip – Justin XL May 12 '11 at 00:41
  • Thank you sooo much Xin. It is now working. So it was the binding after all. This is another proof how inefficient ElementName is. I am glad in Silverlight 5 they are introducing FindAncestor like in WPF. This would make this Proxy workaround obsolete. Until then this is a great workaround. :) – Houman May 12 '11 at 08:08
  • yeah i thought the proxy is not needed in SL4 but... glad to help.:) – Justin XL May 12 '11 at 09:02