Search code examples
wpfcanvasscrollvieweritemspaneltemplate

Click, Drag, and Scroll Canvas View


I have a ListBox with a ItemsPanelTemplate of Canvas. I know the ScrollViewer will not work with the Canvas unless its given a height and width. I DO NOT want to give the canvas a height and width because it will not always be constant. Is there any other work around or tricks anyone has gotten to work for this situation. I know I can't be the only one with this problem. Thanks in advance here is my code so far.

Another problem is I am unable to place the ScrollViewer inside the ItemsPanelTemplate because it can only have one element nested inside.

This also restricts me from placing the canvas inside a grid to get positioning.

XAML:

    <!--Core Viewer-->
    <ScrollViewer x:Name="scrollViewer"
                  VerticalScrollBarVisibility="Hidden"
                  HorizontalScrollBarVisibility="Hidden">

        <ListBox x:Name="objCoreViewer"
             ItemsSource="{Binding ItemsSource}"
             Background="LightGray"
             SelectionChanged="objCoreViewer_SelectionChanged"
             ItemTemplateSelector="{DynamicResource CoreViewerDataTemplateSelector}"
             ItemContainerStyleSelector="{DynamicResource ItemContainerStyleSelector}"
             PreviewMouseWheel="objCoreViewer_PreviewMouseWheel">

            <!-- Core Map Canvas -->

            <ListBox.ItemsPanel>

                <ItemsPanelTemplate>
                    <Canvas x:Name="objCoreViewerCanvas"
                            Background="Transparent">
                        <Canvas.LayoutTransform>
                            <ScaleTransform ScaleX="{Binding Path=Value, ElementName=ZoomSlider}"
                                            ScaleY="{Binding Path=Value, ElementName=ZoomSlider}" />
                        </Canvas.LayoutTransform>
                    </Canvas>
                </ItemsPanelTemplate>

            </ListBox.ItemsPanel>

        </ListBox>

    </ScrollViewer>

Solution

  • To get the canvas to grow based on the the child elements inside of it you will need to override the Canvas's MeasureOverride event in a custom class that inherits from Canavas. This code works great for me:

     protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint)
        {
            Size toReport = new Size();
    
            foreach (UIElement element in this.InternalChildren)
            {
                //Get the left most and top most point.  No using Bottom or Right in case the controls actual bottom and right most points are less then the desired height/width
                var left = Canvas.GetLeft(element);
                var top = Canvas.GetTop(element);
    
                left = double.IsNaN(left) ? 0 : left;
                top = double.IsNaN(top) ? 0 : top;
    
                element.Measure(constraint);
    
                Size desiredSize = element.DesiredSize;
    
                if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
                {
                    //left += desiredSize.Width;
                    //top += desiredSize.Height;
    
                    toReport.Width = toReport.Width > left +desiredSize.Width ? toReport.Width : left + desiredSize.Width;
                    toReport.Height = toReport.Height > top+desiredSize.Height ? toReport.Height : top + desiredSize.Height;
                }
    
            }
    
            //Make sure scroll includes the margins incase of a border or something
            toReport.Width += this.Margin.Right;
            toReport.Height += this.Margin.Bottom;
    
            return toReport;
        }