Welcome to MSDN Blogs Sign in | Join | Help

Data Bind Using Only Code Behind

We are all familiar with the use of WPF Binding in XAML. For example, the following lines bind the caption of a Button (variableCaptionButton) to the text of a TextBox (sourceText).

<TextBox x:Name="sourceText" Grid.Row="0" />
<Button x:Name="variableCaptionButton" Grid.Row="1" Content="{Binding ElementName=sourceText, Path=Text}"/>

 

Have there been times when you need to dynamically build or add UI elements from code behind and have a need to also create bindings among those dynamically created UI elements?

Creating binding using only code behind is easy.

Here is the code behind to accomplish the same binding shown in the above XAML code.

Binding binding = new Binding("Text");
binding.Source = sourceText;
variableCaptionButton.SetBinding(Button.ContentProperty, binding);

 

The Binding instance defines the data source (the “sourceText” TextBox), and which data source property (the “Text” property). SetBinding() connects a binding source to the receiving property on the target element (“variableCaptionButton” button).

Additionally, you can also define a value converter and parameter via the properties, Binding.Converter and Binding.ConverterParameter, respectively.

That’s it!

-Tan

Posted by PermanentTan | 0 Comments

Array as a WPF ConverterParameter

A WPF value converter accepts a value and an optional parameter. The parameter can be specified in the XAML binding as a string like the following:

 

<TextBlock Background=”{Binding Path=myElement, Converter={StatisResource MyConverter}, ConverterParameter=myParameterText}” />

 

Instead of one string, what if you want to pass an array of strings as a converter parameter? What about an array of brushes?

The trick is to specify the binding in its own XAML tag and use Binding.ConverterParameter and Array. Here is an example of how to pass an array of Strings and an array of Brushes into a value converter. Note the uses of Binding, Binding.ConverterParameter, and Array.

 

<Window x:Class="ConverterParameters.Window1"
        xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mscor="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:ConverterParameters"
        WindowStartupLocation="CenterScreen"
        Title="Array as ConverterParameter" Height="100" Width="300">
    <Window.Resources>
        <local:IntToStringMux x:Key="IntToStringMux"/>
        <local:IntToBrushMux x:Key="IntToBrushMux"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <TextBlock Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Center" TextAlignment="Center">
            <TextBlock.Text>
                <Binding ElementName="mySelector" Path="SelectedIndex" Converter="{StaticResource IntToStringMux}">
                    <Binding.ConverterParameter>
                        <x:Array Type="mscor:String">
                            <mscor:String>A selected</mscor:String>
                            <mscor:String>B selected</mscor:String>
                            <mscor:String>C selected</mscor:String>
                            <mscor:String>D selected</mscor:String>
                        </x:Array>
                    </Binding.ConverterParameter>
                </Binding>
            </TextBlock.Text>
            <TextBlock.Background>
                <Binding ElementName="mySelector" Path="SelectedIndex" Converter="{StaticResource IntToBrushMux}">
                    <Binding.ConverterParameter>
                        <x:Array Type="Brush">
                            <SolidColorBrush Color="LawnGreen"/>
                            <SolidColorBrush Color="LightSkyBlue"/>
                            <SolidColorBrush Color="LightCoral"/>
                        </x:Array>
                    </Binding.ConverterParameter>
                </Binding>
            </TextBlock.Background>
        </TextBlock>

        <ComboBox x:Name="mySelector" Grid.Row="1" SelectedIndex="0">
            <ComboBox.Items>
                <ComboBoxItem>Item A</ComboBoxItem>
                <ComboBoxItem>Item B</ComboBoxItem>
                <ComboBoxItem>Item C</ComboBoxItem>
                <ComboBoxItem>Item D</ComboBoxItem>
            </ComboBox.Items>
        </ComboBox>
    </Grid>
</Window>

 

 

For completeness, the implementations for the IntToStringMux and IntToBrushMux converters are also included below.

 

[ValueConversion(typeof(int), typeof(String))]
public class IntToStringMux : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        String result = String.Empty;
        int itemIndex = 0;
        String[] itemDescriptions = parameter as String[];

        if (value != null && value.GetType() == typeof(int))
        {
            itemIndex = (int)value;
        }

        if (itemDescriptions != null && itemIndex < itemDescriptions.Length)
        {
            result = itemDescriptions[itemIndex];
        }

        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

[ValueConversion(typeof(int), typeof(Brush))]
public class IntToBrushMux : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Brush result = Brushes.Red;
        int itemIndex = 0;
        Brush[] brushes = parameter as Brush[];

        if (value != null && value.GetType() == typeof(int))
        {
            itemIndex = (int)value;
        }

        if (brushes != null && itemIndex < brushes.Length)
        {
            result = brushes[itemIndex];
        }

        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

 

That’s it!

Hope this helps. -Tan

Posted by PermanentTan | 0 Comments

Setting Initial Focus in XAML

Last time we looked at a tricky problem of setting the initial focus to a specific item in a populated WPF ListView. Most of the time, however, we just want to set the initial focus to a control (like a TextBox or a Button) in our UI. There are a couple of ways to accomplish this.

  1. Hook the Window’s Loaded event and set the focus to the desired control in the code-behind handler
  2. Use FocusManager.FocusedElement

The second solution is easier and can all be done in XAML. For example:

<Window …

    FocusManager.FocusedElement="{Binding ElementName=myTextBox}">

    …

    <TextBox x:Name=”myTextBox”/>

</Window>

Hope this helps. -Tan

Posted by PermanentTan | 0 Comments

Setting the Initial Focus to a ListView item

This blog describes the steps needed to set the initial keyboard focus to an item in a WPF ListView. The items in the list can be static or data bound. The trick is to access the ListViewItem associated with an item and then set keyboard focus to it.

The following are snippets of the code behind for a dialog with a ListView named myListView and selecting and setting the initial focus to the last item in the list.

Sample Code to Populate the ListView

The for loop populates the list view with some sample items. Note the call to FocusAndSelectItem().

public Window1()
{
    InitializeComponent();

    // Add the items
    int itemCount = 50;
    for (int i = 0; i < itemCount; i++)
    {
        ListViewItem item = new ListViewItem();
        item.Content = String.Format(CultureInfo.CurrentCulture, "Item {0}", i);
        myListView.Items.Add(item);
    }

    // Focus and select the last item
    FocusAndSelectItem(itemCount - 1);
}

Setting Focus to a ListViewItem

Because the list view and its items may not even be visible when they are created, the code cannot just access the list view item and immediately sets keyboard focus to it. We actually have to defer the focus setting by calling Dispatcher.BeginInvoke() to call back when the application has completed all its initial work and has reached the idle state. FocusAndSelectItem() does this.

In the actual method to set the keyboard focus TryFocusAndSelectItem(), note that we need to ensure that the list view item is visible by calling ScrollIntoView() before setting focus to it.

/// <summary>
/// Request the focus to be set on the specified list view item
/// </summary>
/// <param name="itemIndex">index of item to receive the initial focus</param>
private void FocusAndSelectItem(int itemIndex)
{
    Dispatcher.BeginInvoke(new FocusAndSelectItemDelegate(TryFocusAndSelectItem), DispatcherPriority.ApplicationIdle, itemIndex);
}

/// <summary>
/// Make sure a list view item is within the visible area of the list view
/// and then select and set focus to it.
/// </summary>
/// <param name="itemIndex">index of item</param>
private void TryFocusAndSelectItem(int itemIndex)
{
    ListViewItem lvi = myListView.Items.GetItemAt(itemIndex) as ListViewItem;
    if (lvi != null)
    {
        myListView.ScrollIntoView(lvi);
        lvi.IsSelected = true;
        Keyboard.Focus(lvi);
    }
}

private delegate void FocusAndSelectItemDelegate(int itemIndex);

 

That’s it. Hope this helps.

-Tan

Posted by PermanentTan | 1 Comments

WPF ListView with Check Boxes and No Clipping

 

    WPF ListView with Check Boxes and No Clipping

     

    Do you need a simple data-bound WPF ListView with check boxed items without text trimming ellipsis and without header columns? I very often do. In this blog, I would like to share with you a simple way to build this control that I call CheckBoxListView.

     

    What We Want

    • Data-bound WPF ListView with a check box at the beginning of each item
    • No column header
    • Item text not clipped and no text-trimming ellipsis
    • Horizontal scrollbar to automatically sized to show the widest item

     

     image

     

    image

     

     

    What We Don't Want

     

     image

     

     

    What We Need to Do

    • Add the Check Boxes
      • To add the check box to each list view item, we must first override the ListView.View to be a GridView and create two GridViewColumns: one for the check box and the other for the item text.
      • Here the XAML code for our ListView:

     

            <ListView Name="myListView">

                <ListView.View>

                    <GridView>

                        <GridView.ColumnHeaderContainerStyle>

                            <Style TargetType="{x:Type GridViewColumnHeader}">

                                <Setter Property="Visibility"

                                        Value="Collapsed"/>

                            </Style>

                        </GridView.ColumnHeaderContainerStyle>

                        <GridViewColumn>

                            <GridViewColumn.CellTemplate>

                                <DataTemplate>

                                    <CheckBox Margin="0"

                                              VerticalAlignment="Center"

                                              IsChecked="{Binding IsChecked}"/>

                                </DataTemplate>

                            </GridViewColumn.CellTemplate>

                        </GridViewColumn>

                        <GridViewColumn>

                            <GridViewColumn.CellTemplate>

                                <DataTemplate>

                                    <TextBlock Margin="0"

                                               Text="{Binding Text}"

                                               Loaded="TextBlock_Loaded"/>

                                </DataTemplate>

                            </GridViewColumn.CellTemplate>

                        </GridViewColumn>

                    </GridView>

                </ListView.View>

            </ListView>

     

    • Hide the Column Headers
      • To hide the column headers, we need to collapse the GridViewColumnHeader.

     

            <ListView Name="myListView">

                <ListView.View>

                    <GridView>

                        <GridView.ColumnHeaderContainerStyle>

                            <Style TargetType="{x:Type GridViewColumnHeader}">

                                <Setter Property="Visibility"

                                        Value="Collapsed"/>

                            </Style>

                        </GridView.ColumnHeaderContainerStyle>

                        <GridViewColumn>

                            ...

                        </GridViewColumn>

                        <GridViewColumn>

                            ...

                        </GridViewColumn>

                    </GridView>

                </ListView.View>

            </ListView>

     

    • Resize the Column Header to the Widest Item
      • In the code-behind, implement the TextBlock Loaded event handler. In this handler, measure the text and resize the (invisible) header column to the widest rendered item. If the widest item is wider than the available width, the list view automatically shows a horizontal scrollbar
      • Since we hook the TextBlock Loaded event, only rendered ListViewItems will be called. This is an important optimization! So if you a have list of 100,000 items and then you grab the scrollbar thumb and quickly scroll from top to bottom, only a small subset of the items is rendered and consequently measured - Not all 100,000 items! As more items come into view, the header column will be resized to the widest item to appropriately show it.

     

            private void TextBlock_Loaded(object sender, RoutedEventArgs e)

            {

                GridView gridView = myListView.View as GridView;

                if (gridView != null && gridView.Columns.Count >= 2)

                {

                    // Calculate the item's desired text width and increase the

                    // text column's width to match the widest text

                    TextBlock tb = (TextBlock)sender;

                    tb.Measure(new Size(Double.MaxValue, Double.MaxValue));

                    double newWidth = tb.DesiredSize.Width;

                    GridViewColumnCollection columns = gridView.Columns;

                    if (newWidth > columns[1].Width ||

                        double.IsNaN(columns[1].Width))

                    {

                        columns[1].Width = newWidth;

                    }

                }

            }

     

    • Remove the Item Cell Padding for Good Looks
      • The cell content presenter has a default margin of about 6 units. Removing this margin makes the gap between the check box and the text look "perfect" on Vista.

     

            private void TextBlock_Loaded(object sender, RoutedEventArgs e)

            {

                GridView gridView = myListView.View as GridView;

                if (gridView != null && gridView.Columns.Count >= 2)

                {

                    ...

     

                    // Remove the text block cell's content presenter built-in

                    // margin for better-looking spacing

                    ContentPresenter contentPresenter = VisualTreeHelper.GetParent(tb) as ContentPresenter;

                    if (contentPresenter != null)

                    {

                        contentPresenter.Margin = new Thickness(0);

                    }

                }

            }

     

    Hey, What About Data-Binding?

    Even though there are a lot of resources on this topic, here is one way that you can encapsulate an item data source and bind the sources to the List View.

     

    First define a class to hold the data for each list view item:

     

        /// <summary>

        /// Encapsulate item data source for a CheckBoxListView item

        /// </summary>

        public class CheckBoxListViewItemSource : INotifyPropertyChanged

        {

            public CheckBoxListViewItemSource(String text)

            {

                m_text = text;

            }

     

            public bool IsChecked

            {

                get { return m_checked; }

                set

                {

                    if (m_checked == value) return;

                    m_checked = value;

                    RaisePropertyChanged("Checked");

                }

            }

     

            public String Text

            {

                get { return m_text; }

                set

                {

                    if (m_text == value) return;

                    m_text = value;

                    RaisePropertyChanged("Text");

                }

            }

     

            public override string ToString()

            {

                return Text;

            }

     

            public event PropertyChangedEventHandler PropertyChanged;

     

            private void RaisePropertyChanged(string propName)

            {

                PropertyChangedEventHandler eh = PropertyChanged;

                if (eh != null)

                {

                    eh(this, new PropertyChangedEventArgs(propName));

                }

            }

     

            private bool m_checked;

            private String m_text;

        }

     

    Then create a list to hold those item data sources:

    In the following sample, I created this list in the constructor of the application window.

     

            public Window1()

            {

                InitializeComponent();

     

                // Fill the list via data-bound item sources

                ObservableCollection<CheckBoxListViewItemSource> itemSources = new ObservableCollection<CheckBoxListViewItemSource>();

                myListView.ItemsSource = itemSources;

                int ii = 1;

                for (int i = 0; i < 10; i++)

                {

                    itemSources.Add(new CheckBoxListViewItemSource(String.Format("Short Item {0}", ii++)));

                }

                for (int i = 0; i < 5; i++)

                {

                    itemSources.Add(new CheckBoxListViewItemSource(String.Format("Long Item {0} with long long long long long long long long long text", ii++)));

                }

            }

     

    Then bind that list to the List View:

     

            public Window1()

            {

                InitializeComponent();

     

                // Fill the list via data-bound item sources

                ObservableCollection<CheckBoxListViewItemSource> itemSources = new ObservableCollection<CheckBoxListViewItemSource>();

                myListView.ItemsSource = itemSources;

                int ii = 1;

                for (int i = 0; i < 10; i++)

                {

                    itemSources.Add(new CheckBoxListViewItemSource(String.Format("Short Item {0}", ii++)));

                }

                for (int i = 0; i < 5; i++)

                {

                    itemSources.Add(new CheckBoxListViewItemSource(String.Format("Long Item {0} with long long long long long long long long long text", ii++)));

                }

            }

     

     

     

     

    That's it for now. Next time, we'll look at a way to set focus to a list view item.

     

    -Tan

     

Posted by PermanentTan | 0 Comments

Giving full trust (revised)

Newer version of caspol.exe expects a more precise URL specification. Here is the revised caspol.exe command to give full trust to a network share //MyDevBox/MyTools:

caspol.exe  -m  -ag 1.2  -url file://\\MyDevBox\MyTools

Note the adding of "\\" and the switching of the "/" to "\" in the URL.

-Tan

Posted by PermanentTan | 1 Comments

Giving full trust to a network share

I work on multiple machines and, very often, I need access to my tool sets on each machine that I work on. This is accomplished by sharing out my tools folder/drive and restrict full permissions to myself. There is a little wrinkle. Network shares by default only get LocalIntranet permissions and that may not be sufficient for some exes and managed assemblies. Fortunately there is the tool "caspol.exe" (normally located in the installed .NET framework folder, \Windows\Microsoft.NET\Framework\vN.N.NNNNN\CasPol.exe). caspol.exe can tell the client computer to give full trust to a network share which in turn enables the execution of exes and loading of assemblies directly from the network share.

Here is how I give full trust to a network share //MyDevBox/MyTools:

caspol.exe  -m  -ag 1.2  -url file://MyDevBox/MyTools/*  FullTrust

Giving full trust is also needed if you share development of Visual Studio projects/solutions across different machines. For example, I would develop code on my dev box and run test on multiple VPCs with the sources/binaries shared with full trust.

Hope this helps.

-Tan

 

 

Posted by PermanentTan | 2 Comments

Determine the last synced version for a folder in TFS

Have you ever need to find the last synced version of a folder in TFS? Here is a quick way:

  • Change directory to YourFolder
  • tf  history  .  /r  /version:W  /stopafter:1  /i

Note the "." (current directory).

This command recursively lists the history for the YourFolder but stops after the first output entry which is the latest synced version. The /version:W scopes the command to just looking at your local workspace version and not the latest on the server.

Hope this helps.

-Tan

Posted by PermanentTan | 1 Comments

Diagnosing side-by-side problems on Vista

There have been times where a program failed to load due to missing dependencies or side-by-side inconsistencies. One of the tool available on Windows Vista is "sxstrace.exe" (normally located in \Windows\System32). sxstrace.exe can help you pin-point which dependent assembly is expected and is not found. Use sxstrace.exe /? to get the full options.

Example usage:

  • Before running the program that failed to start, in a CMD window, run sxstrace in trace mode
    • sxstrace.exe  Trace  -logfile:C:\tmp\MySxSTrace.log
  • In another CMD window, run the program that failed to start. Wait until it errors out.
  • Back on the first CMD window, stop the trace and convert the log
    • sxstrace.exe  Parse  -logfile:C:\tmp\MySxSTrace.log  -outfile:C:\tmp\MySxSTrace.txt
  • You can then look at the parsed log in MySxSTrace.txt. It will be quite obvious which assembly the failed program was looking for and the various locations that were tried.

If you are not on Windows Vista, filemon or procmon can be used to help track down the offending assembly.

Hope that helps.

-Tan

 

Posted by PermanentTan | 1 Comments

How to share VS Macro Projects

One way to share VS Macro projects is to add the projects to source control and have all users mapped the versioned macro projects to a local folder to be used by VS. Here is a rough outline (an except from one of my forum post) on how you can accomplish this goal.

You can do this using a combination of mapping the Macro project local folder to source control and using the Source Control Explorer (or command line tf.exe) for version control operations.

When you create the macro project, specify a local folder like C:\Source\MacroProjects\MacroProjects1. Then use File > Source Control > Workspaces to map the macro projects folder C:\Source\MacroProjects to somewhere on the server like $/TeamProject/MacroProjects. You can then use the Source Control Explorer (or the command line tf.exe) to add and subsequently check-in the entire MacroProjects folder to source control.

Note: Since the IDE may/can hold onto the handle of a loaded macro project file (.vsmacros), you may want to unload the macro project before doing version control operations (like check-ins) or you may get an error about "another process accessing the file".

From this point, the macro projects are on the server and can be shared by other users. Using the Source Control Explorer (or the command line tf.exe), other users just need to add a mapping to their workspace and then do a Get to download the macro projects to their local folder.

That's it!

-Tan

Posted by PermanentTan | 1 Comments

Sorting the work items in the Pending Changes window

The work item channels displays the work items in the order specified by the selected query. This means that you can control the sort order by modifying the query's sort preferences. You can do this from Team Explorer, expand Work Items to your query, activate context menu and select View Query, and then click the Column Options button on the toolbar. From that dialog, you can define the sorting order.

The fields however cannot be changed in the Pending Changes window. Why? We had a usability study back in pre-V1 and the results were to keep the channel simple.

-Tan

Posted by PermanentTan | 2 Comments

WPF Equivalent for WinForms Ampersand (&) to Prefix Access/Accelerator Character

WPF uses an underscore character instead of the ampersand character (like with WinForms) to prefix an access (a.k.a. accelerator or mnemonic) key in the text of its elements like Label and Button.

You can escape the underscore by using two underscores.

The underscore replaces the ampersand because, in XAML, ampersands can easily lead to mistakes and cause problems.

-Tan

Posted by PermanentTan | 1 Comments

Getting a list of TFS files that are different since some date

Have you ever come across a need to find out which TFS version control files that are different since some date or some changeset? In VS2008 + VSTF 2008 (Orcas), there is an easy way using Folder Difference. Let take a look at the following command.

tf folderdiff $/TP/myfolder;C1234 $/TP/myfolder /r /view:different /noprompt

 

This command compares all files under $/TP/myfolder at version C1234 to the latest version and then output the files that are different to the console window. You can remove the /noprompt to get a UI from which you can also copy/paste the file list to Excel.

Instead of a changeset specification Cnnnn, you can specify a date like Dyyyy/mm/dd.

In addition to /view:different, you can try /view:different,sourceonly,target to also list the files that only exist in the source or the target folders.

The Folder Difference is also available from Visual Studio's Source Control Explorer toolbar and context menu.

-Tan

 

Posted by PermanentTan | 1 Comments

How to set MyControl.X and MyControl.Y in a Windows Presentation Foundation (WPF) Canvas?

WPF elements (like Button and Rectangle) do not have the X and Y properties as in the WinForms controls. Using procedural code, to place a WPF element  at a specific XY on a free-form work area like a Canvas, you can use Canvas.SetLeft() and Canvas.SetTop(). Using XAML, you can use the attribute Canvas.Left and Canvas.Top.

Also try using Intellisense on Canvas.Set* to see other available properties that can be changed this way.

For advanced reading, see WPF Dependency Properties. http://msdn2.microsoft.com/en-us/library/ms752914.aspx

-Tan

Posted by PermanentTan | 1 Comments

Default work item query for the Pending Changes window

 

There has been a couple of requests about the logic for determining the default work item query in the Pending Changes window in Visual Studio. Here is how it works.

 

In the Pending Changes window, the work item query uses an MRU list. In general, once a query is run in the work item channel, the last-run query for a team project becomes the default query for that team project in the work item channel.

 

Algorithm for determining the query for the work item channel in VS

  1. If the work item channel already has a query MRU list (ie. work item channel already opened before):
    1. If the team project for the last query is still in TE, the query is used. If the team project is no longer in TE, the next query in the MRU list, whose team project is still in TE, is used.
    2. If no other query is available from MRU, go to step 2.ii
  1. If the work item channel does not have a query MRU list (ie. the very first time opening the work item channel):
    1. The TE active project is used to search for query My Work Items. If found, that is the initial default query. If not found, go to next step.
    2. Starting with the first team project in TE, search each project for query My Work Items. The first My Work Items query found is the initial default query. If one is not found, go to next step.
    3. The first query of the first team project is the initial default query

 

Delete the following cache file to clear the MRU history:

C:\Documents and Settings\<YourUserName>\Local Settings\Application Data\Microsoft\Team Foundation\1.0\Cache\WorkItemHistory_<YourServerGUID>

C:\Documents and Settings\<YourUserName>\Local Settings\Application Data\Microsoft\Team Foundation\2.0\Cache\WorkItemHistory_<YourServerGUID>

Posted by PermanentTan | 1 Comments
More Posts Next page »
 
Page view tracker