Basic WPF Layout question

I am learning WPF and am trying to follow some sort of best practice. I am kind of lost at the moment, and need some direction.

I am creating a very simple app that reads a text file (error log) and splits it into the individual error messages. I want to display these messages (stored in a model object) as a list of messages. Since the list can consist of many items and I want the window to be resizeable, I need a a vertical scroll bar, but I want the content to wrap (i.e. no need for a horizontal scroll bar).

<Window x:Class="ErrorLog.UI.WPF.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="800" Width="1200" Loaded="Window_Loaded">

<StackPanel Name="mainContainer">
    <StackPanel Orientation="Horizontal" Name="Menu">
        <Button Name="Refresh">Refresh</Button>
    </StackPanel>        
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <StackPanel Name="errorMessagePlaceHolder"></StackPanel>
    </ScrollViewer>
</StackPanel>

I am at the moment reading the file in the code behind and adding to the stackPanel as a bunch of textboxes with the value being the error message. I have also added some mouseover effects like this:

private void LoadData()
    {
        IErrorLogReader errorLogReader = new ErrorLogReader();
        var errors = errorLogReader.RetrieveErrors();

        if (errors.Count == 0)
        {
            TextBox noErrors = new TextBox();
            noErrors.Text = "No errors found";
            errorMessagePlaceHolder.Children.Add(noErrors);
        }
        else
        {
            for (var i = errors.Count - 1; i > 0; i--)
            {
                TextBox errorMessage = new TextBox();

                errorMessage.IsReadOnly = true;
                errorMessage.Padding = new Thickness(10);

                errorMessage.Text = errors[i].ErrorMessage;
                errorMessage.TextWrapping = TextWrapping.Wrap;

                errorMessage.MouseEnter += ErrorMessageMouseEnter;
                errorMessage.MouseLeave += ErrorMessageMouseLeave;

                errorMessagePlaceHolder.Children.Add(errorMessage);
            }
        }
    }

    protected void ErrorMessageMouseEnter(object sender, RoutedEventArgs e)
    {
        ((TextBox) sender).Background = Brushes.AntiqueWhite;
    }

    protected void ErrorMessageMouseLeave(object sender, RoutedEventArgs e)
    {
        ((TextBox) sender).Background = null;
    }

So the first things I want to know is:

  • Is the way I am binding ok?
  • Scroll bar is coming up disabled
  • Is the way I am doing the mouseover effect bad?

Cheers.

Answers


Is the way I am binding ok?

It might work, but it's not best practice. Best practice is to use actual data binding. First, you need to replace your StackPanel with something that can be bound to a list. An ItemsControl is the thing closest to a simple StackPanel, other options would be, for example, a ListBox.

<ScrollViewer VerticalScrollBarVisibility="Auto">
    <ItemsControl Name="errorMessageList" />
</ScrollViewer>
private void LoadData()
{
    IErrorLogReader errorLogReader = new ErrorLogReader();
    var errors = errorLogReader.RetrieveErrors();

    errorMessageList.ItemsSource = errors;
}

To specify how you want the error messages to be displayed, you can set a template for the ItemsControl:

<ScrollViewer VerticalScrollBarVisibility="Auto">
    <ItemsControl Name="errorMessageList">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox IsReadOnly="true" ... Text="{Binding ErrorMessage}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

Scroll bar is coming up disabled

You are nesting your ScrollViewer inside a StackPanel... that won't work: The StackPanel takes as much vertical space as it needs, so the ScrollViewer will always have enough space and never show the scroll bar. You need to replace your top-level StackPanel by something that takes only as much space as is available; a DockPanel, for example:

<DockPanel Name="mainContainer">
    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Name="Menu">
        <Button Name="Refresh">Refresh</Button>
    </StackPanel>        
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <StackPanel Name="errorMessagePlaceHolder"></StackPanel>
    </ScrollViewer>
</StackPanel>

Is the way I am doing the mouseover effect bad?

That can be done with a style and a trigger instead. Define the following style:

<Window ...>
    <Window.Resources>
        <Style x:Key="hoverTextBox" TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="AntiqueWhite" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    ...
</Window>

and assign it to your TextBox inside the DataTemplate:

<TextBox IsReadOnly="true" ... Text="{Binding ErrorMessage}"
         Style="{StaticResource hoverTextBox}" />

Need Your Help

Remove done button in MPMoviePlayerController

iphone ios ipad ios4 mpmovieplayercontroller

Is there a possibility to remove the DONE button from the MPMoviePlayerController fullscreen interface?

Rendering Django forms inside an EXTJS tab

ajax django extjs

I was able to successfully render my first django form inside an extjs tab. The form data displayed properly and the form validation appears to be working properly.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.