Click here to Skip to main content
15,867,686 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
What is the best way to show an image or logo mostly on the right side of a ribbon control?
Posted
Updated 12-May-19 5:39am

Honestly I never used this control, but if it works the same as any other WPF control you can add any other control into it using the Content property. That means you can add panels, buttons, images, etc...

For example, the Button control has a Content property. Mostly you just put text in there, but you can assign a whole new control in there like listbox, radiobutton, textbox, ... (Probably not useful but you get the point ;-)).

There are samples on google.

hope this helps.
 
Share this answer
 
Assuming you want the image to appear on the right hand side of each tab, you need to modify the template for the RibbonTab (if you only want it on one particular tab, you should put it on that tab only, by giving the style a name and tying the tab to that style). In the style I've put together here, look for the line
HTML
:<Image HorizontalAlignment="Right" Height="32" Source="Images/LargeIcon.png" Stretch="Fill" VerticalAlignment="Center" Width="32" Margin="0,0,10,0" Opacity="0.43"/>
and replace it with the imagery you need.
HTML
<ribbon:RibbonWindow.Resources>
  <Style TargetType="{x:Type ribbon:RibbonTab}">
    <Setter Property="KeyTipService.IsKeyTipScope" Value="True"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type ribbon:RibbonTab}">
          <Grid x:Name="MainGrid" ClipToBounds="True">
            <Grid.Visibility>
              <TemplateBinding Property="IsSelected">
                <TemplateBinding.Converter>
                  <BooleanToVisibilityConverter/>
                </TemplateBinding.Converter>
              </TemplateBinding>
            </Grid.Visibility>
            <Line x:Name="OuterLeftLine" Stroke="{Binding ContextualTabGroup.Background, RelativeSource={RelativeSource TemplatedParent}}" StrokeThickness="2" X1="0" X2="{Binding TabHeaderLeft, RelativeSource={RelativeSource TemplatedParent}}" Y1="0" Y2="0"/>
            <Line x:Name="OuterRightLine" Stroke="{Binding ContextualTabGroup.Background, RelativeSource={RelativeSource TemplatedParent}}" StrokeThickness="2" X1="{Binding TabHeaderRight, RelativeSource={RelativeSource TemplatedParent}}" X2="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}" Y1="0" Y2="0"/>
            <ScrollViewer x:Name="ScrollViewer">
              <ScrollViewer.Style>
                <Style TargetType="{x:Type ScrollViewer}">
                  <Setter Property="Focusable" Value="False"/>
                  <Setter Property="VerticalScrollBarVisibility" Value="Disabled"/>
                  <Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
                  <Setter Property="CanContentScroll" Value="True"/>
                  <Setter Property="Template">
                    <Setter.Value>
                      <ControlTemplate TargetType="{x:Type ScrollViewer}">
                        <Grid x:Name="Grid" Background="{TemplateBinding Background}">
                          <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                          </Grid.ColumnDefinitions>
                          <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" Grid.ColumnSpan="3" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}"/>
                          <RepeatButton Grid.Column="0" CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Command="ScrollBar.LineLeftCommand" Focusable="False" HorizontalContentAlignment="Left">
                            <RepeatButton.Style>
                              <Style TargetType="{x:Type RepeatButton}">
                                <Setter Property="OverridesDefaultStyle" Value="True"/>
                                <Setter Property="Focusable" Value="False"/>
                                <Setter Property="Width" Value="14"/>
                                <Setter Property="Background" Value="{Binding (ribbon:RibbonControlService.Ribbon).Background, RelativeSource={RelativeSource Self}}"/>
                                <Setter Property="BorderBrush" Value="{Binding (ribbon:RibbonControlService.Ribbon).BorderBrush, RelativeSource={RelativeSource Self}}"/>
                                <Setter Property="Template">
                                  <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type RepeatButton}">
                                      <Border x:Name="OuterBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1" Background="{TemplateBinding Background}" CornerRadius="3,0,0,3">
                                        <Border x:Name="InnerBorder" Background="#80FFFFFF" CornerRadius="3,0,0,3">
                                          <Path x:Name="GlyphPath" Data="M4,0L0,3.5 4,7" Fill="Gray" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                        </Border>
                                      </Border>
                                      <ControlTemplate.Triggers>
                                        <Trigger Property="HorizontalContentAlignment" Value="Right">
                                          <Setter Property="Data" TargetName="GlyphPath" Value="M0,0L4,3.5 0,7"/>
                                          <Setter Property="CornerRadius" TargetName="OuterBorder" Value="0,3,3,0"/>
                                          <Setter Property="CornerRadius" TargetName="InnerBorder" Value="0,3,3,0"/>
                                        </Trigger>
                                        <MultiDataTrigger>
                                          <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HighContrast}" Value="False"/>
                                            <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True"/>
                                          </MultiDataTrigger.Conditions>
                                          <Setter Property="Background" TargetName="InnerBorder" Value="{Binding (ribbon:RibbonControlService.Ribbon).MouseOverBackground, RelativeSource={RelativeSource Self}}"/>
                                        </MultiDataTrigger>
                                        <MultiDataTrigger>
                                          <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HighContrast}" Value="False"/>
                                            <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="True"/>
                                          </MultiDataTrigger.Conditions>
                                          <Setter Property="Background" TargetName="InnerBorder" Value="{Binding (ribbon:RibbonControlService.Ribbon).PressedBackground, RelativeSource={RelativeSource Self}}"/>
                                        </MultiDataTrigger>
                                        <DataTrigger Binding="{Binding HighContrast}" Value="True">
                                          <Setter Property="CornerRadius" TargetName="OuterBorder" Value="0"/>
                                          <Setter Property="BorderBrush" TargetName="OuterBorder" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                                          <Setter Property="Background" TargetName="OuterBorder" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                          <Setter Property="CornerRadius" TargetName="InnerBorder" Value="0"/>
                                          <Setter Property="Background" TargetName="InnerBorder" Value="Transparent"/>
                                          <Setter Property="Fill" TargetName="GlyphPath" Value="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}"/>
                                        </DataTrigger>
                                        <MultiDataTrigger>
                                          <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HighContrast}" Value="True"/>
                                            <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True"/>
                                          </MultiDataTrigger.Conditions>
                                          <Setter Property="Background" TargetName="InnerBorder" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                        </MultiDataTrigger>
                                        <MultiDataTrigger>
                                          <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HighContrast}" Value="True"/>
                                            <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="True"/>
                                          </MultiDataTrigger.Conditions>
                                          <Setter Property="BorderBrush" TargetName="OuterBorder" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>
                                        </MultiDataTrigger>
                                      </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                  </Setter.Value>
                                </Setter>
                              </Style>
                            </RepeatButton.Style>
                            <RepeatButton.Visibility>
                              <MultiBinding ConverterParameter="0" FallbackValue="Visibility.Collapsed" UpdateSourceTrigger="Default">
                                <MultiBinding.Converter>
                                  <Microsoft_Windows_Controls_Ribbon_Primitives:RibbonScrollButtonVisibilityConverter/>
                                </MultiBinding.Converter>
                                <Binding Path="ComputedHorizontalScrollBarVisibility" RelativeSource="{RelativeSource TemplatedParent}"/>
                                <Binding Path="HorizontalOffset" RelativeSource="{RelativeSource TemplatedParent}"/>
                                <Binding Path="ExtentWidth" RelativeSource="{RelativeSource TemplatedParent}"/>
                                <Binding Path="ViewportWidth" RelativeSource="{RelativeSource TemplatedParent}"/>
                              </MultiBinding>
                            </RepeatButton.Visibility>
                          </RepeatButton>
                          <RepeatButton Grid.Column="2" CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Command="ScrollBar.LineRightCommand" Focusable="False" HorizontalContentAlignment="Right">
                            <RepeatButton.Style>
                              <Style TargetType="{x:Type RepeatButton}">
                                <Setter Property="OverridesDefaultStyle" Value="True"/>
                                <Setter Property="Focusable" Value="False"/>
                                <Setter Property="Width" Value="14"/>
                                <Setter Property="Background" Value="{Binding (ribbon:RibbonControlService.Ribbon).Background, RelativeSource={RelativeSource Self}}"/>
                                <Setter Property="BorderBrush" Value="{Binding (ribbon:RibbonControlService.Ribbon).BorderBrush, RelativeSource={RelativeSource Self}}"/>
                                <Setter Property="Template">
                                  <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type RepeatButton}">
                                      <Border x:Name="OuterBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1" Background="{TemplateBinding Background}" CornerRadius="3,0,0,3">
                                        <Border x:Name="InnerBorder" Background="#80FFFFFF" CornerRadius="3,0,0,3">
                                          <Path x:Name="GlyphPath" Data="M4,0L0,3.5 4,7" Fill="Gray" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                        </Border>
                                      </Border>
                                      <ControlTemplate.Triggers>
                                        <Trigger Property="HorizontalContentAlignment" Value="Right">
                                          <Setter Property="Data" TargetName="GlyphPath" Value="M0,0L4,3.5 0,7"/>
                                          <Setter Property="CornerRadius" TargetName="OuterBorder" Value="0,3,3,0"/>
                                          <Setter Property="CornerRadius" TargetName="InnerBorder" Value="0,3,3,0"/>
                                        </Trigger>
                                        <MultiDataTrigger>
                                          <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HighContrast}" Value="False"/>
                                            <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True"/>
                                          </MultiDataTrigger.Conditions>
                                          <Setter Property="Background" TargetName="InnerBorder" Value="{Binding (ribbon:RibbonControlService.Ribbon).MouseOverBackground, RelativeSource={RelativeSource Self}}"/>
                                        </MultiDataTrigger>
                                        <MultiDataTrigger>
                                          <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HighContrast}" Value="False"/>
                                            <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="True"/>
                                          </MultiDataTrigger.Conditions>
                                          <Setter Property="Background" TargetName="InnerBorder" Value="{Binding (ribbon:RibbonControlService.Ribbon).PressedBackground, RelativeSource={RelativeSource Self}}"/>
                                        </MultiDataTrigger>
                                        <DataTrigger Binding="{Binding HighContrast}" Value="True">
                                          <Setter Property="CornerRadius" TargetName="OuterBorder" Value="0"/>
                                          <Setter Property="BorderBrush" TargetName="OuterBorder" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                                          <Setter Property="Background" TargetName="OuterBorder" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                          <Setter Property="CornerRadius" TargetName="InnerBorder" Value="0"/>
                                          <Setter Property="Background" TargetName="InnerBorder" Value="Transparent"/>
                                          <Setter Property="Fill" TargetName="GlyphPath" Value="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}"/>
                                        </DataTrigger>
                                        <MultiDataTrigger>
                                          <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HighContrast}" Value="True"/>
                                            <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True"/>
                                          </MultiDataTrigger.Conditions>
                                          <Setter Property="Background" TargetName="InnerBorder" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                        </MultiDataTrigger>
                                        <MultiDataTrigger>
                                          <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HighContrast}" Value="True"/>
                                            <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="True"/>
                                          </MultiDataTrigger.Conditions>
                                          <Setter Property="BorderBrush" TargetName="OuterBorder" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>
                                        </MultiDataTrigger>
                                      </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                  </Setter.Value>
                                </Setter>
                              </Style>
                            </RepeatButton.Style>
                            <RepeatButton.Visibility>
                              <MultiBinding ConverterParameter="100" FallbackValue="Visibility.Collapsed" UpdateSourceTrigger="Default">
                                <MultiBinding.Converter>
                                  <Microsoft_Windows_Controls_Ribbon_Primitives:RibbonScrollButtonVisibilityConverter/>
                                </MultiBinding.Converter>
                                <Binding Path="ComputedHorizontalScrollBarVisibility" RelativeSource="{RelativeSource TemplatedParent}"/>
                                <Binding Path="HorizontalOffset" RelativeSource="{RelativeSource TemplatedParent}"/>
                                <Binding Path="ExtentWidth" RelativeSource="{RelativeSource TemplatedParent}"/>
                                <Binding Path="ViewportWidth" RelativeSource="{RelativeSource TemplatedParent}"/>
                              </MultiBinding>
                            </RepeatButton.Visibility>
                          </RepeatButton>
                        </Grid>
                      </ControlTemplate>
                    </Setter.Value>
                  </Setter>
                </Style>
              </ScrollViewer.Style>
              <Grid RenderOptions.ClearTypeHint="Enabled">
                <Canvas x:Name="BackgroundCanvas" HorizontalAlignment="Left" Height="0" Margin="0,1,0,0" VerticalAlignment="Top" Width="0">
                  <Rectangle x:Name="OpaqueRect" Fill="{Binding (ribbon:RibbonControlService.Ribbon).Background, RelativeSource={RelativeSource Self}}" Height="{Binding ActualHeight, ElementName=ScrollViewer}" Width="{Binding ActualWidth, ElementName=ScrollViewer}"/>
                  <Rectangle x:Name="OverlayRect" Height="{Binding ActualHeight, ElementName=ScrollViewer}" Width="{Binding ActualWidth, ElementName=ScrollViewer}">
                    <Rectangle.Fill>
                      <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="#EEFFFFFF" Offset="0"/>
                        <GradientStop Color="#BBFFFFFF" Offset="0.1"/>
                        <GradientStop Color="#05FFFFFF" Offset="0.5"/>
                        <GradientStop Color="#20FFFFFF" Offset="1"/>
                      </LinearGradientBrush>
                    </Rectangle.Fill>
                  </Rectangle>
                  <Rectangle x:Name="InnerOverlayRect" Fill="#60FFFFFF" Height="{Binding ActualHeight, ElementName=ScrollViewer}" Width="{Binding ActualWidth, ElementName=ScrollViewer}"/>
                </Canvas>
                <Image HorizontalAlignment="Right" Height="32" Source="Images/LargeIcon.png" Stretch="Fill" VerticalAlignment="Center" Width="32" Margin="0,0,10,0" Opacity="0.43"/>
                <ItemsPresenter x:Name="ItemsPresenter"/>
              </Grid>
            </ScrollViewer>
          </Grid>
          <ControlTemplate.Triggers>
            <Trigger Property="CanContentScroll" SourceName="ScrollViewer" Value="False">
              <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=ScrollViewer}"/>
              <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=ScrollViewer}"/>
              <Setter Property="Canvas.Top" TargetName="OverlayRect" Value="{Binding VerticalOffset, ElementName=ScrollViewer}"/>
              <Setter Property="Canvas.Left" TargetName="OverlayRect" Value="{Binding HorizontalOffset, ElementName=ScrollViewer}"/>
              <Setter Property="Canvas.Top" TargetName="InnerOverlayRect" Value="{Binding VerticalOffset, ElementName=ScrollViewer}"/>
              <Setter Property="Canvas.Left" TargetName="InnerOverlayRect" Value="{Binding HorizontalOffset, ElementName=ScrollViewer}"/>
            </Trigger>
            <MultiDataTrigger>
              <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding HighContrast}" Value="False"/>
                <Condition Binding="{Binding ContextualTabGroup, RelativeSource={RelativeSource Self}}" Value="{x:Null}"/>
              </MultiDataTrigger.Conditions>
              <Setter Property="Stroke" TargetName="OuterLeftLine" Value="{Binding Ribbon.BorderBrush, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ribbon:RibbonTab}}}"/>
              <Setter Property="Stroke" TargetName="OuterRightLine" Value="{Binding Ribbon.BorderBrush, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ribbon:RibbonTab}}}"/>
            </MultiDataTrigger>
            <DataTrigger Binding="{Binding HighContrast}" Value="True">
              <Setter Property="Visibility" TargetName="BackgroundCanvas" Value="Collapsed"/>
              <Setter Property="Stroke" TargetName="OuterLeftLine" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>
              <Setter Property="Stroke" TargetName="OuterRightLine" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>
            </DataTrigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ribbon:RibbonWindow.Resources>
 
Share this answer
 
This is an old question but I want to share also my solution in case others are still looking for an alternate solution without need of any xaml.

It inherits the RibbonTab and allows to set an image/company logo on the right side of the tab. Furthermore it checks on size changes if the image can be shown without overlapping the RibbonGroups.

VB.NET
Imports System.Reflection

Public Class ExRibbonTab
    Inherits Ribbon.RibbonTab

    Public Shared ReadOnly ImageSourceProperty As DependencyProperty = DependencyProperty.Register("ImageSource", GetType(ImageSource), GetType(ExRibbonTab))
    Public Shared ReadOnly ImageHorizontalMarginProperty As DependencyProperty = DependencyProperty.Register("ImageHorizontalMargin", GetType(Double), GetType(ExRibbonTab))

    ''' <summary>Gets or sets the image used on right side of this RibbonTab (i.e. Company logo).</summary>
    Property ImageSource As ImageSource = Nothing
    ''' <summary>Gets or sets the image horizontal margin (=left and right space; default=20).</summary>
    Property ImageHorizontalMargin As Double = 20


    Private mImage As Image
    Private mScrollGrid As Grid

    Dim dpiXProperty = GetType(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic Or BindingFlags.[Static])
    Dim dpiYProperty = GetType(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic Or BindingFlags.[Static])
    Private dpiX As Integer = CInt(dpiXProperty.GetValue(Nothing, Nothing))
    Private dpiY As Integer = CInt(dpiYProperty.GetValue(Nothing, Nothing))


    Protected Overrides Sub OnInitialized(e As EventArgs)
        MyBase.OnInitialized(e)
        _setImage()
    End Sub

    Private Sub _setImage()
        ' Search for ScrollViewer in case ImageSource is set
        Me.Dispatcher.BeginInvoke(Sub()
                                      Dim scroll As ScrollViewer = Me.Template.FindName("ScrollViewer", Me)
                                      If scroll Is Nothing OrElse ImageSource Is Nothing Then Return

                                      mScrollGrid = scroll.Content
                                      AddHandler mScrollGrid.SizeChanged, AddressOf _sizeChanged

                                      ' Create new BitmapImage from ImageSource
                                      Dim bmp As BitmapImage = ImageSource
                                      mImage = New Image
                                      With mImage
                                          .Margin = New Thickness(ImageHorizontalMargin / 2, 0, ImageHorizontalMargin / 2, 0)
                                          .HorizontalAlignment = HorizontalAlignment.Right
                                          .VerticalAlignment = VerticalAlignment.Center
                                          .Width = bmp.DecodePixelHeight / dpiY * 96
                                          .Height = bmp.DecodePixelWidth / dpiX * 96
                                          .Source = bmp
                                      End With

                                      ' Insert image into grid before ItemsPresenter
                                      mScrollGrid.Children.Insert(1, mImage)
                                      _showImage(Nothing, mScrollGrid.ActualWidth)

                                      If Me.Items.Count > 0 Then
                                          ' Add handler to last RibbonGroup to track size changes
                                          AddHandler CType(Me.Items(Me.Items.Count - 1), Ribbon.RibbonGroup).SizeChanged, AddressOf _sizeChanged
                                      End If
                                  End Sub)
    End Sub

    Private Sub _showImage(sender As Object, newWidth As Double)
        If newWidth = 0 Then Return ' Return if there is no width 

        ' Check if this tab is also the selected tab otherwise return
        If mScrollGrid.Children.Count < 2 OrElse Not Me.Ribbon.SelectedValue Is Me Then Return

        Dim x As Double = mScrollGrid.Children(2).DesiredSize.Width ' use width from itemspresenter

        ' Check if image fits in available tab width
        If newWidth > x + mImage.Width + ImageHorizontalMargin + 5 Then
            mImage.Visibility = Visibility.Visible
        Else
            mImage.Visibility = Visibility.Hidden
        End If

    End Sub

    Private Sub _sizeChanged(sender As Object, e As SizeChangedEventArgs)
        ' Ensure image visibility depending on ribbon size
        Me.Dispatcher.BeginInvoke(Sub()
                                      If TypeOf sender Is Ribbon.RibbonGroup Then
                                          _showImage(sender, CType(sender.Parent, Ribbon.RibbonTab).ActualWidth)
                                      Else
                                          If Not e.NewSize.Width = e.PreviousSize.Width Then _showImage(sender, e.NewSize.Width)
                                      End If
                                  End Sub)
    End Sub
End Class
 
Share this answer
 
v3
Comments
Richard MacCutchan 12-May-19 11:41am    
It might get better visibility if you posted it (with full details) as a tip.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900