0

I'd like to change the color of certain cells and access the buttons of different cells.

I want to do this when the Loaded event of the datagrid is called.

Current datagrid:

<DataGrid SelectionUnit="FullRow" SelectionMode="Single" AutoGenerateColumns="False" Height="468" HorizontalAlignment="Left" Margin="12,0,0,238" Name="LockDataGrid" VerticalAlignment="Bottom" Width="554" LoadingRow="LockDataGrid_LoadingRow" CanUserAddRows="False">
    <DataGrid.Columns>
        <DataGridTextColumn IsReadOnly="True" Header="Name" Width="200" Binding="{Binding Name}"></DataGridTextColumn>
        <DataGridTextColumn IsReadOnly="True" Header="ModuleLock" Binding="{Binding ModuleLock}"></DataGridTextColumn>
        <DataGridTextColumn IsReadOnly="True" Header="StringLock" Binding="{Binding StringLock}"></DataGridTextColumn>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button>Lock module string</Button>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button>Lock strings</Button>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

Loaded method:

    private void LockDataGrid_Loaded(object sender, RoutedEventArgs e)
    {
        DataGrid grid = (DataGrid)sender;
    }

How do Iterate over the rows and cells?

NomenNescio
  • 2,899
  • 8
  • 44
  • 82
  • 3
    Instead of iterating through the `DataGrid`'s items (which is... pretty annoying due to the WPF DataGrid`s structure), you should define a generic `Style` for your cells, with `Triggers` deciding on how to display your cells according to their contents. This would be the "clean" way to do it imho – Damascus Aug 16 '12 at 15:42
  • I'd like to iterate over trough code, I'd like to understand the underlying structure. – NomenNescio Aug 16 '12 at 15:45
  • [This answer](http://stackoverflow.com/a/11615729/620360) may help you start understanding. But I totally agree with @Damascus. – LPL Aug 16 '12 at 15:55
  • @Damascus Besides, triggers can't do everything I want. – NomenNescio Aug 16 '12 at 15:55
  • You can also use a binding with a converter to set the cells background and the visibility of the buttons. – franssu Aug 16 '12 at 16:03
  • Why wouldn't triggers do what you want? What are you trying to do which is beyond the Triggers' scope? @LPL I also use your method, but it won't work on some cases (ie DataGrid hasn't already been rendered). Since he's working with the Loaded event, wouldn't it fail getting the VisualChild if the DataGrid hasn't been rendered yet? – Damascus Aug 16 '12 at 16:09
  • @Damascus According to MSDN [FrameworkElement.Loaded Event](http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.loaded%28v=vs.100%29.aspx) "occurs when the element is laid out, *rendered*, and ready for interaction". I think it should work. – LPL Aug 16 '12 at 16:38
  • @LPL Strangely enough, I got an opposite behavior last time. Create a TabControl with one `DataGrid` in each tab. The `DataGrid` which is in the unselected tab is loaded, but not rendered because your `TabControl` would render its content only if you select it. I couldn't iterate through the VisualTree in that way. (I actually had this problem with `AvalonDock`, but I think it'd be the same with `TabControl` – Damascus Aug 16 '12 at 16:42

1 Answers1

1

I'm going to come right out and say that you probably don't want to walk the visual tree to achieve your goal (particularly if it is to only do something with the buttons / set cell color)...

The controls on the rows will come in and out of existance as the datagrid is scrolled by the user meaning you would need to constantly monitor and update this (for small numbers of rows this probably wont happen, but it should give indication that this isn't a good way to approach things, but LPL has linked to a relevant answer for that if you really are needing to go that route).

In order to do something when the buttons are clicked, then you would want to setup commands on those buttons and bind them in your data model; this page is a tutorial application which describes and shows how to bind commands MSDN: WPF Apps With The Model-View-ViewModel Design Pattern

You can then use a style with a binding or datatrigger to alter the background color on a cell (among many other things). I've knocked together a quicky example to show this in action (although I haven't used command binding for this small example, but I do highly recommend doing so).

Firstly you xaml modified to have a background color bind, and to also trigger a function on Button click of ModuleLock.

    <DataGrid SelectionUnit="FullRow" SelectionMode="Single" AutoGenerateColumns="False"   Name="LockDataGrid"  CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridTextColumn IsReadOnly="True" Header="Name" Width="200" Binding="{Binding Name}">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Setter Property="Background" Value="{Binding CellColor}" />
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
            <DataGridTextColumn IsReadOnly="True" Header="ModuleLock" Binding="{Binding ModuleLock}"></DataGridTextColumn>
            <DataGridTextColumn IsReadOnly="True" Header="StringLock" Binding="{Binding StringLock}"></DataGridTextColumn>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Click="Button_Click">Lock module string</Button>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button>Lock strings</Button>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

Next the button click handler in the code behind, in this example I'm simply only going to set the CellColor property of the row data to Red. As mentioned above a command could be setup for the button which can be configured to be much more relevant (e.g. the commandparameter could be set to the DataContext which in the default case will be the data for the row).

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var lockModuleButton = sender as Button;
        if (lockModuleButton == null)
        {
            return;
        }

        var theRowData = lockModuleButton.DataContext as Class1;
        if (theRowData == null)
        {
            return;
        }

        theRowData.CellColor = "Red";

    }

And one final important part - The data class (where I've created the CellColor property) will need to notify of property changes so that the binding updates INotifyPropertyChanged (also note I've only bothered to setup the CellColor to update bindings when it changes):

using System.ComponentModel;
namespace WpfApplication7
{
class Class1 : INotifyPropertyChanged
{
    private string cellColor;

    public string CellColor
    {
        get
        {
            return cellColor;
        }
        set
        {
            cellColor = value;
            OnPropertyChanged("CellColor");
        }
    }


    public bool ModuleLock { get; set; }
    public bool StringLock { get; set; }
    public string Name { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    }
}
}

And here is the quick code in the window open event to dummy up some data and set the datacontext of the DataGrid:

    public MainWindow()
    {
        InitializeComponent();

        List<Class1> MyList = new List<Class1>();

        MyList.Add(new Class1());
        MyList.Add(new Class1());
        MyList.Add(new Class1());
        MyList.Add(new Class1());
        MyList.Add(new Class1());
        MyList.Add(new Class1());

        LockDataGrid.ItemsSource = MyList;

    }

Edit: While building this I spotted that the actual problem you're wanting to solve is to lock down access to those string fields when the button is pressed (it may be worth updating your question to reflect this if that is the case, and if so I will refine this answer down if it meets your needs or is along the right track).

Unfortunately it appears that the DataGrid isn't well designed for this (from what I can see anyway) - however it can be done by using DataGridTemplateColumn With the following for the column:

            <DataGridTemplateColumn Header="ModuleLock">

                <DataGridTemplateColumn.CellStyle>
                    <Style TargetType="{x:Type DataGridCell}">
                        <Style.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="IsEditing" Value="True" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </DataGridTemplateColumn.CellStyle>

                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Background="{Binding CellColor}" Text="{Binding ModuleLock}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>

                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <TextBox Background="{Binding CellColor}" BorderThickness="0" IsReadOnly="{Binding ModuleLockFlag}" Text="{Binding ModuleLock}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>

            </DataGridTemplateColumn>

The style (LPL will recognise it ;) is there because there is strange focusing behaviour with the mouse; unfortunately you will want something else for a slight odd behaviour with using tab focusing that can be found here:

Community
  • 1
  • 1
Alternator
  • 154
  • 1
  • 2
  • 11