5
\$\begingroup\$

I just redid Rubberduck's About window in XAML:

Rubberduck's About Window

I do not like my XAML very much. The way I place the StackPanel for the white pane over two rows, only to provide a margin to keep the copyright text out of it seems very hackish. Just as bad is the way I put the Hyperlink elements in a textbox (they are not allowed to be placed directly in a StackPanel because it is not a UIElement), with the XAML-drawn logo (courtesy of ThunderFrame) inside. Is there a cleaner way to do either of these?

<UserControl x:Class="Rubberduck.UI.About.AboutControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:about="clr-namespace:Rubberduck.UI.About"
             mc:Ignorable="d" 
             d:DesignHeight="350" d:DesignWidth="499"
             d:DataContext="{d:DesignInstance {x:Type about:AboutControlViewModel}, IsDesignTimeCreatable=False}">
    <UserControl.Resources>
        <BitmapImage x:Key="DuckImage" UriSource="../../Resources/RD-AboutWindow.png" />
    </UserControl.Resources>
    <Grid Background="#F6F6F6F6">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="180" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="55" />
        </Grid.RowDefinitions>
        <StackPanel HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Margin="10,10,0,10"
                    DockPanel.Dock="Top">
            <Image Source="{StaticResource DuckImage}"
                    Margin="0,0,0,5"
                    Width="170"
                    HorizontalAlignment="Center" />
            <TextBlock HorizontalAlignment="Center">
            <Hyperlink Name="WebpageHyperlink"
                        NavigateUri="http://rubberduckvba.com/"
                        Command="{Binding UriCommand}"
                        CommandParameter="{Binding ElementName=WebpageHyperlink, Path=NavigateUri}">http://rubberduckvba.com/</Hyperlink>
            </TextBlock>
        </StackPanel>
        <StackPanel Grid.Column="1"
                    Grid.Row="0"
                    Background="White"
                    Margin="10,10,10,30"
                    Grid.RowSpan="2">
            <TextBlock Margin="10"
                       Text="{Binding Version}"
                       FontWeight="Bold"
                       VerticalAlignment="Top"
                       HorizontalAlignment="Left"
                       FontSize="14" />

            <Label Content="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=AboutWindow_AttributionsLabel}"
                   FontWeight="SemiBold"
                   FontSize="12"
                   Margin="5,0,0,0" />
            <TextBlock Margin="15,0,0,10"
                       Text="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=AboutWindow_AttributionList}"
                       FontSize="10"/>

            <Label Content="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=AboutWindow_SpecialThanksLabel}"
                   FontWeight="SemiBold"
                   FontSize="12"
                   Margin="5,0,0,0" />
            <ScrollViewer Margin="15,0,10,10"
                          Height="150">
                <TextBlock Text="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=AboutWindow_SpecialThanksList}"
                           FontSize="10" />
            </ScrollViewer>
        </StackPanel>

        <StackPanel HorizontalAlignment="Left"
                    Grid.Row="1"
                    Grid.Column="0"
                    Grid.ColumnSpan="2"
                    Margin="10"
                    Orientation="Horizontal">
            <!-- Circle logos created with Ai->XAML Export Plug-In Version 0.2 (PC/64) -->
            <!-- By Mike Swanson (http://blog.mikeswanson.com/)           -->
            <TextBlock>
                <Hyperlink Name="GitHubUrl"
                           NavigateUri="https://github.com/rubberduck-vba/Rubberduck"
                           Command="{Binding UriCommand}"
                           CommandParameter="{Binding ElementName=GitHubUrl, Path=NavigateUri}"
                           TextDecorations="{x:Null}">
                    <Viewbox Width="35"
                             Height="35">
                        <Canvas Width="120"
                                Height="120"
                                Background="Transparent">
                                <Path Fill="Black"
                                      Data=" M 59.996,0.000 C 26.866,0.000 0.000,26.863 0.000,60.003 C 0.000,86.510 17.190,108.999 41.035,116.934 C 44.035,117.484 45.130,115.632 45.130,114.041 C 45.130,112.617 45.078,108.844 45.049,103.837 C 28.359,107.463 24.837,95.794 24.837,95.794 C 22.109,88.862 18.175,87.017 18.175,87.017 C 12.728,83.294 18.588,83.370 18.588,83.370 C 24.609,83.794 27.778,89.553 27.778,89.553 C 33.130,98.722 41.821,96.074 45.240,94.539 C 45.785,90.661 47.335,88.017 49.049,86.517 C 35.725,85.003 21.719,79.856 21.719,56.863 C 21.719,50.313 24.057,44.957 27.895,40.763 C 27.278,39.245 25.219,33.145 28.483,24.884 C 28.483,24.884 33.521,23.271 44.982,31.035 C 49.768,29.704 54.901,29.039 60.003,29.016 C 65.099,29.039 70.232,29.704 75.025,31.035 C 86.479,23.271 91.508,24.884 91.508,24.884 C 94.781,33.145 92.722,39.245 92.105,40.763 C 95.950,44.957 98.272,50.313 98.272,56.863 C 98.272,79.913 84.244,84.985 70.877,86.471 C 73.032,88.323 74.951,91.985 74.951,97.582 C 74.951,105.603 74.877,112.074 74.877,114.041 C 74.877,115.646 75.958,117.513 79.003,116.927 C 102.824,108.977 120.000,86.503 120.000,60.003 C 120.000,26.863 93.134,0.000 59.996,0.000 Z"/>
                        </Canvas>
                    </Viewbox>
                </Hyperlink>
                <Hyperlink Name="TwitterUrl"
                           NavigateUri="http://www.twitter.com/rubberduckvba"
                           Command="{Binding UriCommand}"
                           CommandParameter="{Binding ElementName=TwitterUrl, Path=NavigateUri}"
                           TextDecorations="{x:Null}">
                    <Viewbox Margin="10,0,0,0"
                             Width="35"
                             Height="35">
                        <Canvas Width="120"
                                Height="120"
                                Background="Transparent">
                                <Path Fill="Black"
                                      Data="F1 M 92.670,42.747 C 92.702,43.460 92.718,44.177 92.718,44.897 C 92.718,66.860 76.001,92.185 45.431,92.185 C 36.045,92.185 27.310,89.433 19.954,84.718 C 21.255,84.871 22.577,84.950 23.919,84.950 C 31.706,84.950 38.872,82.293 44.560,77.836 C 37.287,77.701 31.149,72.896 29.034,66.293 C 30.049,66.487 31.090,66.591 32.161,66.591 C 33.677,66.591 35.145,66.388 36.540,66.009 C 28.937,64.482 23.208,57.765 23.208,49.712 C 23.208,49.642 23.208,49.572 23.209,49.503 C 25.450,50.748 28.013,51.496 30.737,51.582 C 26.278,48.601 23.344,43.514 23.344,37.748 C 23.344,34.703 24.163,31.848 25.594,29.393 C 33.791,39.448 46.037,46.065 59.850,46.758 C 59.567,45.541 59.420,44.273 59.420,42.970 C 59.420,33.792 66.861,26.351 76.040,26.351 C 80.820,26.351 85.139,28.369 88.171,31.599 C 91.957,30.854 95.514,29.471 98.725,27.566 C 97.484,31.447 94.849,34.704 91.417,36.761 C 94.779,36.359 97.982,35.466 100.963,34.144 C 98.735,37.477 95.917,40.404 92.670,42.747 Z M 60.000,0.000 C 26.863,0.000 0.000,26.863 0.000,60.000 C 0.000,93.137 26.863,120.000 60.000,120.000 C 93.137,120.000 120.000,93.137 120.000,60.000 C 120.000,26.863 93.137,0.000 60.000,0.000 Z"/>
                        </Canvas>
                    </Viewbox>
                </Hyperlink>
            </TextBlock>
        </StackPanel>
            <TextBlock Grid.Row="1"
                       Grid.Column="1"
                       Text="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=AboutWindow_Copyright}"
                       FontSize="8"
                       VerticalAlignment="Bottom"
                       Margin="10,0,0,10"/>
    </Grid>
</UserControl>

I am satisfied with my VM, but here it is for completeness. If anything can be improved, please bring it to my attention.

public class AboutControlViewModel
{
    public string Version
    {
        get
        {
            var name = Assembly.GetExecutingAssembly().GetName();
            return string.Format(RubberduckUI.Rubberduck_AboutBuild, name.Version, name.ProcessorArchitecture);
        }
    }

    private ICommand _uriCommand;
    public ICommand UriCommand
    {
        get
        {
            if (_uriCommand != null)
            {
                return _uriCommand;
            }
            return _uriCommand = new DelegateCommand(uri =>
            {
                Process.Start(new ProcessStartInfo(((Uri)uri).AbsoluteUri));
            });
        }
    }
}
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$
private ICommand _uriCommand;
public ICommand UriCommand
{
    get
    {
        if (_uriCommand != null)
        {
            return _uriCommand;
        }
        return _uriCommand = new DelegateCommand(uri =>
        {
            Process.Start(new ProcessStartInfo(((Uri)uri).AbsoluteUri));
        });
    }
}

There's no reason the ICommand can't be readonly. I don't like having a getter performing assignments like this. Is the cost of instantiating a DelegateCommand such that lazy-initializing the field is warranted? I don't think so.

This is much cleaner IMO:

private readonly ICommand _uriCommand;
public ICommand UriCommand { get { return _uriCommand; } }

And the constructor can do the assignment, like it does with every other private readonly field out there, ever:

_uriCommand = new DelegateCommand(ExecuteUriCommand);

This also eliminates the "return the result of an assignment expression", which I've never come across the absolute requirement to do.

And, the rest of Rubberduck commands are implemented with ExecuteXxxxxCommand/CanExecuteXxxxxCommand full-fledged methods, which makes debugging easier if needed. An inline delegate really only serves to clutter things up and make things harder to work with when problems arise.

private void ExecuteUriCommand(object parameter)
{
    Process.Start(new ProcessStartInfo(((Uri)parameter).AbsoluteUri));
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.