How to get a ListView with expandable controls to resize

I created a ListView and used an expandable control for each of the ListView’s items. Everything was fine until I noticed that after collapsing an item the ListView did not resize. Resizing worked fine in the other direction, i.e. the Listview would grow in size when an item was expanded. I thought that I was going to have to do all sorts of jiggery pokery with MeasureOverride or the such like but actually the solution was really simple.

Just set the ItemsPanel to a type that automatically resizes:

<ListView.ItemsPanel>
  <ItemsPanelTemplate>
    <StackPanel/>
  </ItemsPanelTemplate>
</ListView.ItemsPanel>

So here is my ListView in its entirety:

<ListView x:Name="lvMenus" AllowDrop="True" ItemContainerStyle="{DynamicResource ItemContStyle}"
    		HorizontalAlignment="Stretch">
	<ListView.ItemsPanel>
		<ItemsPanelTemplate>
			<StackPanel/>
		</ItemsPanelTemplate>
	</ListView.ItemsPanel>
	<ListView.View>
		<GridView>
			<GridViewColumn Header="Titles">
				<GridViewColumn.CellTemplate>
					<DataTemplate>
						<spEtr:Inspector
					          AssetId="{Binding Id, Mode=OneWay}"
						  LongTitle="{Binding LongTitle, Mode=TwoWay}"
						  LongTitleChanged="Inspector_LongTitleChanged"
						  LongTitleLostFocus="Inspector_LongTitleLostFocus"
						  LongTitleGotFocus="Inspector_LongTitleGotFocus"
						  ShortTitle="{Binding ShortenedTitle, Mode=TwoWay}"
						  ShortTitleChanged="Inspector_ShortTitleChanged"
						  ShortTitleFocused="Inspector_ShortTitleFocused"
						  ShortTitleLostFocus="Inspector_ShortTitleLostFocus"
						  ExpanderClicked="Inspector_ExpanderClicked"
						  LongTitleErrorStatusChanged="Inspector_LongTitleErrorStatusChanged"
							/>
					</DataTemplate>
				</GridViewColumn.CellTemplate>
			</GridViewColumn>
		</GridView>
	</ListView.View>
</ListView>

WPF Using a trigger when Validation has error

There is much documentation out there regarding how to use ValidationRules together with an ErrorTemplate in order to highlight invalid user data. However, I found it a little trickier to find out how to trigger some other kind of behaviour.

Here is my scenario:
I have two text boxes, the second of which is collapsed unless a validation error occurs in the first text box. This seems like pretty much a run of the mill scenario but it was a little trickier to get working than simply putting a red border round the first text box.

I put all the validation and triggers in a Style.  Here is the XAML describing the two text boxes:

<StackPanel Name="ExpanderStackPnl" MinWidth="440">
  <Expander x:Name="TitleExpander"
                      Header="{Binding ElementName=InspectorRoot, Path=LongTitle}"
              Collapsed="TitleExpander_Collapsed" Expanded="TitleExpander_Expanded">
    <StackPanel Margin="5,5,5,5" x:Name="StackInsideExpander">
		   <spCntrls:SportTitle x:Name="spTitle"
                             SpLabel="Desired title (fits on most platforms):"
                             Style="{StaticResource LongTitleStyle}"
                             TitleChanged="LongTitle_Changed"
                             LostFocus="LongTitle_LostFocus"
                             ErrorStatusChanged="LongTitle_ErrorStatusChanged"
                              GotFocus="LongTitle_GotFocus">

          <spCntrls:SportTitle.UserText>
            <Binding Path="LongTitle"
                              ElementName="InspectorRoot"
                              UpdateSourceTrigger="PropertyChanged"
                              NotifyOnValidationError="True"
                             Mode="TwoWay">
              <Binding.ValidationRules>
                <rules:FitsAllValidationRule/>
              </Binding.ValidationRules>
            </Binding>
          </spCntrls:SportTitle.UserText>
        </spCntrls:SportTitle>

        <spCntrls:SportTitle x:Name="spShortTitle"
                             SpLabel="Abbreviated Title (must fit ALL platforms)"
                             Style="{StaticResource ShortTitleStyle}"
                             TitleChanged="ShortTitle_Changed"
                             GotFocus="ShortTitle_GotFocus"
                             LostFocus="ShortTitle_LostFocus">
          <spCntrls:SportTitle.UserText>
            <Binding Path="ShortTitle"
                              ElementName="InspectorRoot"
                              UpdateSourceTrigger="PropertyChanged"
                              NotifyOnValidationError="True"
                             Mode="TwoWay">
              <Binding.ValidationRules>
                <rules:FitsAllValidationRule/>
              </Binding.ValidationRules>
            </Binding>
          </spCntrls:SportTitle.UserText>
        </spCntrls:SportTitle>
     </StackPanel>
   </Expander>
</StackPanel>

As you can see, I have attached a style to each of them and that is where the behaviour is described.  I used a GlobalDictionary to store the styles creating a namespace to refer to the class (named Inspector) where my text boxes reside.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:edtr="clr-namespace:BBCNewsI.Cps2Dtext.SportEditor"
    xmlns:spCntrls="clr-namespace:BBCNewsI.Cps2Dtext.SportEditor.Controls">

The XAML which is of interest is in the style named ShortTitleStyle. Using a DataTrigger you can trigger on changes to another control:

<DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=spTitle}" Value="true">
	<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=spTitle}" Value="false">
	<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>

The thing that I found interesting is that a sibling control is simply referred to by its name, there is no need for any fancy namespace references or Ancestor like binding.

You might have noticed that these controls are inside an expander. This creates in interesting problem when there is an error and the expander is collapsed, namely that you can still see the border of the error template! To get round this, I used another DataTrigger with a dependency property which indicates the expanded state of the expander to set the error template to null:

<DataTrigger Binding="{Binding Path=TitleExpanded,
        RelativeSource={RelativeSource AncestorType={x:Type spCntrls:Inspector}}}"
                   Value="False">
  <Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
 </DataTrigger>

<DataTrigger Binding="{Binding Path=TitleExpanded,
        RelativeSource={RelativeSource AncestorType={x:Type spCntrls:Inspector}}}"
                   Value="True">
   <Setter Property="spCntrls:SportTitle.ToolTip"
                Value="Enter title for scene, both text fields usually have the same text " +
                      "unless the desired title is too long to fit on all platforms"/>
</DataTrigger>

Here are both styles that have been applied to the text boxes in their entirety:

<Style x:Key="LongTitleStyle" TargetType="{x:Type spCntrls:SportTitle}">
    <Setter Property="Validation.ErrorTemplate">
      <Setter.Value>
        <ControlTemplate x:Name="InspectorErrorTemplate">
          <StackPanel Orientation="Vertical">
            <Border BorderBrush="Goldenrod" BorderThickness="1">
              <AdornedElementPlaceholder Name="adornerPlaceholder"/>
            </Border>
          </StackPanel>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
    <Style.Triggers>
      <DataTrigger Binding="{Binding Path=TitleExpanded,
        RelativeSource={RelativeSource AncestorType={x:Type spCntrls:Inspector}}}"
                   Value="False">
        <Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
      </DataTrigger>

      <DataTrigger Binding="{Binding Path=TitleExpanded,
        RelativeSource={RelativeSource AncestorType={x:Type spCntrls:Inspector}}}"
                   Value="True">
        <Setter Property="spCntrls:SportTitle.ToolTip"
                Value="Enter title for scene, both text fields usually have the same " +
			"text unless the desired title is too long to fit on all platforms"/>
     </DataTrigger>

      <Trigger Property="Validation.HasError" Value="true">
        <Setter Property="spCntrls:SportTitle.UserTextForeground" Value="Maroon"/>
        <Setter Property="spCntrls:SportTitle.HasError" Value="true"/>
        <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
          Path=(Validation.Errors)[0].ErrorContent}"/>
      </Trigger>
      <Trigger Property="Validation.HasError" Value="false">
        <Setter Property="spCntrls:SportTitle.HasError" Value="false"/>
      </Trigger>
    </Style.Triggers>
  </Style>

  <!-- Style for short version of the title -->
  <Style x:Key="ShortTitleStyle" BasedOn="{StaticResource LongTitleStyle}"
							TargetType="{x:Type spCntrls:SportTitle}">
    <Setter Property="Validation.ErrorTemplate">
      <Setter.Value>
        <ControlTemplate x:Name="InspectorErrorTemplate">
          <StackPanel Orientation="Vertical">
            <Border BorderBrush="Red" BorderThickness="1">
              <AdornedElementPlaceholder Name="adornerPlaceholder"/>
            </Border>
          </StackPanel>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
    <Style.Triggers>
      <Trigger Property="Validation.HasError" Value="true">
        <Setter Property="spCntrls:SportTitle.UserTextForeground" Value="Red"/>
        <Setter Property="ToolTip" Value="Alternative title is not short enough"/>
      </Trigger>

      <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=spTitle}" 
               Value="true">
        <Setter Property="Visibility" Value="Visible"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=spTitle}" 
               Value="false">
        <Setter Property="Visibility" Value="Collapsed"/>
      </DataTrigger>
    </Style.Triggers>
  </Style>

Unauthorised operation whilst saving remote configuration file

I encountered an interesting problem whilst trying to save changes to a configuration file that is not on the local machine.

When calling Configuration.Save(), dot net creates a new copy of the configuration file which has the changed information, the original is hived away to a temp location and it’s security permissions are then copied to the new file before the original is deleted and the new file renamed to be the same as the old file.

From MSDN documentation:

When ‘Creator Owner’ is listed in the ACL (Access Control List) of the directory containing the configuration file, the current user of Save becomes the new owner of the file and inherits the permissions granted to ‘Creator Owner’. This results in an elevation of privileges for the current user and a removal of privileges for the previous owner.

Deeper down Configuration.Save() calls

System.Configuration.Internal.WriteFileContext.DuplicateFileAttributes(String source, String destination)

Take a look at the function:

private void DuplicateTemplateAttributes(string source, string destination)
{
   if (this.IsWinNT)
   {
     FileSecurity accessControl = File.GetAccessControl(source, AccessControlSections.Access);
     accessControl.SetAccessRuleProtection(accessControl.AreAccessRulesProtected, true);
     File.SetAccessControl(destination, accessControl);
   }
  else
  {
    FileAttributes FileAttributes fileAttributes = File.GetAttributes(source); File.SetAttributes(destination, fileAttributes );
  }
}

The work around is to use Configuration.SaveAs and then copy this version over the original file which is almost the same as the Save method but doesn’t require taking ownership of the file or copying the permissions to the new file.

Getting started

Well this is my first blog.  OK, OK so I’m a bit slow to get blogging but the trouble is I’m not sure that anyone will be at all interested in anything I’ve got to say.  Maybe this might give me some idea if that is true – ha ha!

It’s going to seem pretty all over the place though.  I had thought that I would use it to post technical stuff, coding and the like but then I thought that maybe I should just write about whatever I please.  So here goes, it’s going to be “different”.