1

I write

 <TabItem Header="Map">
     <Grid Background="#FFE5E5E5" Margin="0,0,0,0">
      <ScrollViewer x:Name="mapScroll" HorizontalAlignment="Left" Height="248" Margin="10,10,0,0" VerticalAlignment="Top" Width="467" HorizontalScrollBarVisibility="Auto"/>
     </Grid>
    </TabItem>

and add in code

Canvas ca=new Canvas();
 mapScroll.Content=ca;

and add

 Button btnTest1 = new Button();
     btnTest1.Width = 100;
     btnTest1.Height = 150;
     Canvas.SetLeft(btnTest1, 200);
     Canvas.SetTop(btnTest1, 200);
ca.Children.Add(btnTest1);

The button was located in boundary area of canvas. Side of button is not visible, and scrollbar is not activated. I want to resize canvas size to include full button, so scrollbar should be activated... ScollViewer is in same size.

How i resize Canvas, so scrollbar has to activate.

Lightstar
  • 183
  • 3
  • 12
  • possible duplicate of [WPF: How to make canvas auto-resize?](http://stackoverflow.com/questions/855334/wpf-how-to-make-canvas-auto-resize) – Sphinxxx Dec 14 '14 at 14:43

1 Answers1

0

I assume that amount of code will put off most people so I post solution at the beginning, here. My concept is to make Height of Canvas dependent on Canvas.Children.Count. That would change Canvas' height every time collection changes. But unfortunately, Children.Count is not Dependency Property and therefore it does not reflect any change. I implemented custom canvas with ChildrenCount property which does let knows about change. Another issue is that Canvas does not provide any event for adding item to its children so I was made to override OnVisualChildrenChanged event from base class. I count on your understanding for this amount of code.

public class CustomCanvas : Canvas, INotifyPropertyChanged
{
    private int childrenCount;

    public int ChildrenCount
    {
        get { return childrenCount; }
        set
        {
            childrenCount = value;
            OnPropertyChanged();
        }
    }

    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {
        if (visualAdded == null)
            --ChildrenCount;
        else
            ++ChildrenCount;
        base.OnVisualChildrenChanged(visualAdded, visualRemoved);
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

XAML:

 <StackPanel>
    <StackPanel.Resources>
        <local:ChildrenCountToHeightConverter x:Key="ChildrenCountToHeightConverter"/>
    </StackPanel.Resources>
    <ScrollViewer HorizontalAlignment="Left" Height="248" Margin="10,10,0,0" VerticalAlignment="Top" Width="467"
                  HorizontalScrollBarVisibility="Auto"
          VerticalScrollBarVisibility="Auto">
        <local:CustomCanvas x:Name="CustomCanvas" MaxHeight="1555">
            <local:CustomCanvas.Height>
                <MultiBinding Converter="{StaticResource ChildrenCountToHeightConverter}">
                    <Binding RelativeSource="{RelativeSource Self}" Path="(local:CustomCanvas.ChildrenCount)"/>
                    <Binding RelativeSource="{RelativeSource Self}" Path="(local:CustomCanvas.Children)"/>
                </MultiBinding>
            </local:CustomCanvas.Height>
        </local:CustomCanvas>
    </ScrollViewer>
    <Button Content="dodaj" Click="ButtonBase_OnClick"/>
</StackPanel>


 private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        Button btnTest1 = new Button();
        btnTest1.Width = 100;
        btnTest1.Height = 150;
        Canvas.SetLeft(btnTest1, 200);
        Canvas.SetTop(btnTest1, 200);
        CustomCanvas.Children.Add(btnTest1);
    }


public class ChildrenCountToHeightConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var childrenNumber = System.Convert.ToInt32(values[0].ToString());
        if (childrenNumber == 0)
            return 0;
        var children = (UIElementCollection)values[1];
        var y = children.OfType<Button>().Max(x => Canvas.GetTop(x));
        return y + children.OfType<Button>().First(x => Canvas.GetTop(x) == y).Height + 50;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I am sure it demands some changes but right now it works. Once I add button canvas' height is adjusted based on button placed at the bottom.

Maximus
  • 3,458
  • 3
  • 16
  • 27
  • Thankyou. This code work very well. But i have to add Image and TextBlock, so i changed 'children.OfType – Lightstar Dec 15 '14 at 15:35
  • And I want to adapt this to not only vertical but also Horizontal alignment. – Lightstar Dec 15 '14 at 15:38
  • If you want many objects with diffrent type just add their basic class like FrameworkElement instead of Image or Button. If you want Width as well you need to add the same binding as for height but add another converter in which you will be seeking for Canvas.GetLeft(obj). Mark it as answear if it its helpful. – Maximus Dec 15 '14 at 18:47