Click here to Skip to main content
15,891,725 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
So to be frank I have previously posed this on StackOverflow without getting any particularly helpfull answers, I accept there might be arguably better ways of structuring the code but I want partly to understand the limitations of the Canvas object (if indeed that is the problem) and as there were two
voices on SO suggesting the use of "ItemControls as layers", why that method might be a better solution.

So, I was intending to overlay multiple Canvas, between 4 and 6 layers, on top of a large Image, in order that I can set all objects in a given Canvas as visible or invisable with the simplicity of a show or Hide routine in a layer class. UpdateLayers simply has a set of calls to each layer.Update(). In the case of the settlementNames layer, it would seem that the Update code is not doing its job. It is supposed work like this;
C#
private void ShowCities_Click(object sender, RoutedEventArgs e)
{
    UpdateLayers();
    settlements.Show(Settlements);
    settlementNames.Show(SettlementNames);
}

public void Show(Canvas canvas)
{
    canvas.Visibility = Visibility.Visible;
}


This worked perfectly with the first canvas containing icon sized BitmapImages at ZIndex 1 (the large Image is essentially the background with ZIndex 0). When I tried to add a further canvas at ZIndex 2, the code steps through as expected but does not show the contents. This time the contents is a set of TextBlocks.

The AssociatedCanvas property in the code, has been checked and is the correct Canvas instance, which was laid down in the XAML main window.

C#
public void Update(string layerSelectSqlQuery, LayerType layerType)
{
    DataTable layerDataTable = null;
    int x = -1;
    int y = -1;
    string label;

    using (MySqlClientWrapper db = new MySqlClientWrapper("Server = localhost; Database = tribes;Uid = root;Pwd = xxxxxxxxx;"))
    {
        // TODO add population column - and filter to those settlements considered cities.
        layerDataTable = db.GetDataTable(layerSelectSqlQuery);
    }

    AssociatedCanvas.Children.Clear();

    foreach (DataRow dataRow in layerDataTable.Rows)
    {
        x = (int)dataRow["MapX"];
        y = (int)dataRow["MapY"];
        label = dataRow["Name"].ToString();
        if (x != -1 && y != -1)
        {
            switch (layerType)
            {
                case LayerType.Settlements:
                    DrawBitmapImage(x, y);
                    break;

                case LayerType.SettlementNames:
                    WriteLabel(x, y, label, Color.FromRgb(0, 0, 0));
                    break;

                case LayerType.Units:
                    break;

                case LayerType.UnitNames:
                    break;

                default:
                    break;
            }
        }
    }
}  


Public void WriteLabel(int x, int y, string text, Color color)
{
    TextBlock textBlock = new TextBlock();
    textBlock.Text = text;
    textBlock.Foreground = new SolidColorBrush(color);
    Canvas.SetLeft(textBlock, x);
    Canvas.SetTop(textBlock, y);
    AssociatedCanvas.Children.Add(textBlock);
}


The XAML looks like this in part:

XML
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <!--<Slider Grid.Column="0" Orientation="Vertical" HorizontalAlignment="Left" Minimum="1" x:Name="slider" />-->
    <ScrollViewer Name="mapScroller" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <Grid Name="grid" RenderTransformOrigin="0.5,0.5">
            <Grid.LayoutTransform>
                <TransformGroup>
                    <ScaleTransform x:Name="scaleTransform" />
                </TransformGroup>
            </Grid.LayoutTransform>
            <Viewbox Grid.Column="0" Grid.Row="0" >
                <Image x:Name="MainMap" UseLayoutRounding="True" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" 
                       MouseLeftButtonUp="MainMap_MouseLeftButtonUp" Source="{Binding MainTerrainMap}"></Image>
            </Viewbox>
            <Canvas x:Name="Settlements" Panel.ZIndex="1" />
            <Canvas x:Name="SettlementNames" Panel.ZIndex="2" >
            </Canvas>
        </Grid>
    </ScrollViewer>
</Grid>


What I have tried:

I had wondered if the size of the Image and thus the Canvas was an issue, 21600 x 10800 pixels, because after replacing this with a much smaller 400 x 400 Image, smaller than the ScrollViewer would normally be, the show hide functionality for the second canvas ("SettlmentNames") worked . But bare in mind I could show/hide the lower ZOrder Canvas ("Settlements") without problem. Perhaps a memory limitation then?
Posted
Updated 14-Jan-19 10:00am
v3

1 solution

Why are you even bothering with "multiple canvases"?

"Layers" are a logical construct.

If there is nothing (extra) to see, then simply assign a "layer #" to each component and "process a layer" by iterating over the same layer # (hiding, showing, coloring, whatever).

Think in terms of filters.
 
Share this answer
 
Comments
Member 11830457 14-Jan-19 19:11pm    
Thanks for coming in on this Gerry.

This is a trial and error thing at the moment, about finding alternative routes to achieve the functionality required.

So as you might guess I'm new to WPF.
I figured hiding a canvas might be a more efficient method than iterating through what might be several thousand items, although of course I would be able to limit that by an as yet undetermined number, by only drawing items that are visable in the scrollviewer.

I think your suggestion is probably the best bet, if simply showing/hiding the Canvas with one instruction is not an option. But nonetheless I am curious to know what the problem is with multiple Canvas.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900