(note - reposting from my question on the CommunityToolkit github repo which doesn't seem very active - see https://github.com/CommunityToolkit/Windows/discussions/459 for original)
I'm currently struggling with getting an Image to centre inside a SettingsCard, so I'm hoping someone can give me some pointers...
I'm kind of new to the Community Toolkit controls, and tbh I'm kind of new to WinUI / Xaml in general so I may be trying to do something daft - let me know if there's better way of doing what I'm doing.
This is what I'm hoping to do... I have a SettingsExpander
with a nested SettingsCard
that contains an Image
control. I'd like the Image
to be centred horizontally within the SettingsCard
:
(arrowed lines just for illustration - not part of the UI)
Here's the Xaml I'm using, which is as close as I've been able to get to my desired layout.
<UserControl
... snip ...
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
>
<controls:SettingsGroup>
<tkcontrols:SettingsExpander
x:Uid="MouseUtils_MouseJump_Appearance"
HeaderIcon="{ui:FontIcon Glyph=}"
IsEnabled="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=OneWay}"
IsExpanded="False">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard
HorizontalAlignment="Center">
<Image Source="{x:Bind Path=ViewModel.MouseJumpPreviewImage, Mode=OneWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>
</UserControl>
However, this rendered as:
The problems I have are:
When using HorizontalAlignment="Center"
in the SettingsCard, it seems to no longer fill the width of the SettingsExpander - there's light grey background visible either side of it.
The Image isn't perfectly centred in the SettingsCard - I think this is because there's space reserved for the HeaderIcon, but I've not been able to work out how to remove that space (HeaderIcon="" causes an exception, and I'm not sure what other values / settings I could use to remove the HeaderIcon).
This UserControl is part of a much bigger project I'm contributing to, so it's possible there's some global configuration settings being applied that I'm unaware of - I can give links to source if needed.
Any suggestions on what to try to fix this, or an alternative approach to achieve the same thing would be welcome...
I managed to put a workaround together - it's not pretty but it works.
The first part was to wrap the image in a Grid
and StackPanel
to allow it to centre property inside the "content" area of the SettingsCard
:
<UserControl
... snip ...
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
>
<controls:SettingsGroup>
<tkcontrols:SettingsExpander
x:Uid="MouseUtils_MouseJump_Appearance"
HeaderIcon="{ui:FontIcon Glyph=}"
IsEnabled="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=OneWay}"
IsExpanded="False">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard
x:Name="MouseUtils_MouseJump_PreviewImage"
MinHeight="300"
MaxHeight="300"
Loaded="PreviewImage_Loaded">
<Grid
MinHeight="283"
MaxHeight="283"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Source="{x:Bind Path=ViewModel.MouseJumpPreviewImage, Mode=OneWay}" Stretch="None" />
</StackPanel>
</Grid>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>
</UserControl>
The second part was to inspect the source code for the CommunityToolkit library and find the names of the private / internal controls so I could use get references to them and hide them - this expands the "content" area to fill the entire SettingsCard
:
private void PreviewImage_Loaded(object sender, RoutedEventArgs e)
{
bool TryFindFrameworkElement(SettingsCard settingsCard, string partName, out FrameworkElement result)
{
result = settingsCard.FindDescendants()
.OfType<FrameworkElement>()
.FirstOrDefault(
x => x.Name == partName);
return result is not null;
}
/*
apply a variation of the "Left" VisualState for SettingsCards
to center the preview image in the true center of the card
see https://github.com/CommunityToolkit/Windows/blob/9c7642ff35eaaa51a404f9bcd04b10c7cf851921/components/SettingsControls/src/SettingsCard/SettingsCard.xaml#L334-L347
*/
var settingsCard = (SettingsCard)sender;
var partNames = new List<string>
{
"PART_HeaderIconPresenterHolder",
"PART_DescriptionPresenter",
"PART_HeaderPresenter",
"PART_ActionIconPresenter",
};
foreach (var partName in partNames)
{
if (!TryFindFrameworkElement(settingsCard, partName, out var element))
{
continue;
}
element.Visibility = Visibility.Collapsed;
}
if (TryFindFrameworkElement(settingsCard, "PART_ContentPresenter", out var content))
{
Grid.SetRow(content, 1);
Grid.SetColumn(content, 1);
content.HorizontalAlignment = HorizontalAlignment.Center;
}
}
This results in the following, with the image properly centred in the otherwise-empty SettingsCard
:
There's a danger this might break if the internals of the SettingsCard
library change, but it works for the time being, and I couldn't see a way to do it using just the public interface...