4

I have made a grid programmatically, and now I wan't to somehow hilite the row+column the mouse is over.

I was looking for a mouseOver/Enter/Leave, or IsMouseOver... on the column, but it seems they are not there...

here the Xaml code

<UserControl
x:Class="moo_data_sheets.Views.MoraleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:moo_data_sheets.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">

<Grid x:Name="MoraleGrid" />

</UserControl>

And the relevant code behind:

MoraleGrid.ColumnDefinitions.Add(new ColumnDefinition()); // Header
MoraleGrid.RowDefinitions.Add(new RowDefinition()); // Header

// Create columns
List<int> morales = new List<int>();
for (int i = 100; i > 40; i -= 5)
    morales.Add(i);

foreach (var item in morales)
    MoraleGrid.ColumnDefinitions.Add(new ColumnDefinition()); // Header

// Create Rows
List<int> population = new List<int>();
for (int i = 1; i <= 20; i++)
    population.Add(i);

foreach (var item in population)
    MoraleGrid.RowDefinitions.Add(new RowDefinition()); // Header
...

I am new to UWP, so I am not really sure what I am looking for, any help appreciated.

Update - 1 I tried the method suggested by David Oliver, but it gives me some problems.

The effect I wish for, is a bit of highlight for the row/column that I mouse-over.

My original code already adds borders for coloring, The following snippet is the original code

// Function for creating the color I like for the morale table as function
// of the strike size.
Func<int, Border> getStrikeSizeBorder = (strike) =>
{
    Dictionary<int, Brush> colors = new Dictionary<int, Brush>()
    {
        {0, new SolidColorBrush(Windows.UI.Color.FromArgb(155, 183, 255, 205)) },
        {1, new SolidColorBrush(Windows.UI.Color.FromArgb(155, 252, 232, 178)) },
        {2, new SolidColorBrush(Windows.UI.Color.FromArgb(155, 244, 199, 195)) },
        {3, new SolidColorBrush(Windows.UI.Color.FromArgb(155, 255, 153, 0)) }
    };

    return new Border
    {
        Background = colors.ContainsKey(strike)
            ? colors[strike]
            : new SolidColorBrush(Windows.UI.Color.FromArgb(255, 255, 0, 255))
    };
};

// Fill the table with the actual values
// The trick is the casting to int the floors the value.
for (int i = 0; i < morales.Count; ++i)
{
    for (int j = 0; j < population.Count; ++j)
    {
        int strikeSize = (100 - morales[i]) * population[j] / 100;

        var item = getStrikeSizeBorder(strikeSize);
        item.Child = CreateTextBlockCenteredText(strikeSize.ToString()); ;

        Grid.SetRow(item, j + 1);
        Grid.SetColumn(item, i + 1);
        MoraleGrid.Children.Add(item);
    }
}

This produces the following output Strike Size as function of population and morale

I have tried the following to highlight the Column/Row

for (int i = 1; i < MoraleGrid.ColumnDefinitions.Count; i++)
{
    var border = new Border { Background = new SolidColorBrush(Colors.Transparent) };
    MoraleGrid.Children.Add(border);

    Grid.SetColumn(border, i);
    Grid.SetRow(border, 1);
    Grid.SetRowSpan(border, MoraleGrid.RowDefinitions.Count - 1);
    border.PointerEntered += (o, e) =>
        border.Background = new SolidColorBrush(Colors.Tomato);

    border.PointerExited += (o, e) =>
        border.Background = new SolidColorBrush(Colors.Transparent);
}

for (int i = 1; i < MoraleGrid.RowDefinitions.Count; i++)
{
    var border = new Border { Background = new SolidColorBrush(Colors.Transparent) };
    MoraleGrid.Children.Add(border);

    Grid.SetColumn(border, 1);
    Grid.SetRow(border, i);
    Grid.SetColumnSpan(border, MoraleGrid.ColumnDefinitions.Count - 1);
    border.PointerEntered += (o, e) =>
        border.Background = new SolidColorBrush(Colors.Tomato);

    border.PointerExited += (o, e) =>
        border.Background = new SolidColorBrush(Colors.Transparent);
}

but only the last set of added items are visible, and only the last 'mouseover' affected items are 'effective', for instance, the row, as seen below:

Image where row is highlighted

The following snippet, based on @DavidOliver's answer solved the problem

...
// When making the relevant border items, I add them to a list for later
activeElements.Add(item);
...
Action<Border> AddHighLight = (I) =>
{
    var rightOfI = activeElements
        .Where(X =>
                Grid.GetColumn(X) < Grid.GetColumn(I)
                && Grid.GetRow(X) == Grid.GetRow(I))
        .ToArray();

    var aboveI = activeElements
        .Where(X =>
                Grid.GetColumn(X) == Grid.GetColumn(I)
                && Grid.GetRow(X) < Grid.GetRow(I))
        .ToArray();

    double w = 0.5;
    I.PointerEntered += (o, e) =>
    {
        I.BorderThickness = new Thickness(0, 0, w, w);
        foreach (var item in aboveI)
            item.BorderThickness = new Thickness(w, 0, w, 0);
        foreach (var item in rightOfI)
            item.BorderThickness = new Thickness(0, w, 0, w);
    };

    I.PointerExited += (o, e) =>
    {
        I.BorderThickness = new Thickness(0);
        foreach (var item in aboveI)
            item.BorderThickness = new Thickness(0);
        foreach (var item in rightOfI)
            item.BorderThickness = new Thickness(0);
    };
};

foreach (var item in activeElements)
{
    AddHighLight(item);
}

The following is the result, hovering over {75%,11} which I am quite happy about Morale, hovering mouse over 75%,11

buildcomplete
  • 552
  • 2
  • 15

1 Answers1

4

There are the PointerEntered and PointerExited events, but these are on views, not rows/columns. You would add a view to each row+column intersection and use it to display the visual effect you desire.

For example:

for (int i = 0; i < MoraleGrid.ColumnDefinitions.Count; i++)
{
    for (int j = 0; j < MoraleGrid.RowDefinitions.Count; j++)
    {
        var border = new Border { Background = new SolidColorBrush(Colors.Transparent) };
        MoraleGrid.Children.Add(border);

        Grid.SetColumn(border, i);
        Grid.SetRow(border, j);

        border.PointerEntered += (o, e) =>
            {
                border.Background = new SolidColorBrush(Colors.Tomato);
            };
        border.PointerExited += (o, e) =>
            {
                border.Background = new SolidColorBrush(Colors.Transparent);
            };
    }
}

Note that a more idiomatic way to do this would be to set a 'PointerOver' VisualState from the events, and then handle the details of the effect in a Storyboard in Xaml. You can see this approach in the default styles for some framework controls, such as ToggleButton.

UPDATE: In the code you added, the row highlights are uppermost in the visual tree (because they're added last) so they mask the column highlights from receiving pointer events.

Since you're already putting borders in each 'cell,' I would just add the pointer handling logic there:

    var item = getStrikeSizeBorder(strikeSize);
    item.PointerEntered += (o, e) =>
      {
          var column = Grid.GetColumn(item);
          foreach (var child in MoraleGrid.Children.Where(child => Grid.GetColumn((FrameworkElement)child) == column))
          {
              //Set highlight colour
          }

          ...etc
      };
    item.PointerExited += (o, e) =>
      {
           ...
      }
David Oliver
  • 2,251
  • 12
  • 12