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.