I've got something working.
The first column width must be "*".
The second must be "auto" (assuming your buttons have the same width).
The grid width must now be altered based on the available space left in the window.
We do this with a converter.
<grid width="{Binding ., UpdateSourceTrigger=PropertyChanged, Converter={local:GridWidthConverter}}"></grid>
<Grid Margin="10">
<TreeView Name="trvMenu">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MenuItem}" ItemsSource="{Binding Items}">
<Border BorderBrush="Yellow" Padding="5" BorderThickness="1">
<Grid Width="{Binding ., UpdateSourceTrigger=PropertyChanged, Converter={local:GridWidthConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border BorderBrush="AliceBlue" Padding="5" Grid.Column="0" BorderThickness="1">
<TextBlock Text="{Binding Title}" />
</Border>
<Border BorderBrush="Red" Padding="5" Grid.Column="1" BorderThickness="1">
<Button Width="80" Height="25" >C</Button>
</Border>
</Grid>
</Border>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
The converter needs the level of the item so it can calculate the indent from the left of the screen.
So I added this to the class:
public class MenuItem
{
public MenuItem()
{
this.Items = new ObservableCollection<MenuItem>();
}
public string Title { get; set; }
public int level;
public ObservableCollection<MenuItem> Items { get; set; }
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MenuItem root = new MenuItem() { Title = "Menu", level=1 };
MenuItem childItem1 = new MenuItem() { Title = "Child item #1", level=2 };
childItem1.Items.Add(new MenuItem() { Title = "Child item #1.1", level=3 });
childItem1.Items.Add(new MenuItem() { Title = "Child item #1.2",level=3 });
root.Items.Add(childItem1);
root.Items.Add(new MenuItem() { Title = "Child item #2", level=2 });
trvMenu.Items.Add(root);
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
trvMenu.BeginInit();
trvMenu.EndInit();
}
Now for the converter:
class GridWidthConverter : System.Windows.Markup.MarkupExtension, IValueConverter
{
public GridWidthConverter()
{
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
MenuItem menuItem = (MenuItem)value;
return (App.Current as App).TreeViewWindow.ActualWidth - (App.Current as App).TreeViewIndent - (App.Current as App).ExtraSpace - (menuItem.level * (App.Current as App).TreeViewIndent);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return true;
}
private static GridWidthConverter instance;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (instance == null)
instance = new GridWidthConverter();
return instance;
}
}
The converter returns: Window.ActualWidth - treeview indent - extraSpace (margins, padding, ....) - (level * treeview indent).
public partial class App : Application
{
public Window TreeViewWindow;
public double TreeViewIndent = 20;
public double ExtraSpace = 50;
}
Now all the buttons of all nodes will be aligned to the right side of the window.
It's kind of a hack.
So I would not go with a treeview, but use a listbox with a usercontrol if possible.
(I have no idea how to put a screenshot in the "answer")