Best wpf questions in May 2012

I want a design alternative to a singleton

9 votes

I realize there is much discussion about singletons and why that are bad. That is not what this question is about. I understand the drawbacks to singletons.

I have a scenario where using a singleton is easy and appears to make sense. However, I want an alternative that will accomplish what I need without a lot of overhead.

Our application is designed as a client that typically runs on laptops in the field and communicates with a back end server. We have a status bar at the bottom of the main application. It contains a few text areas that show various statues and information as well as several icons. The icons change their image to indicate their state. Such as a GPS icon that indicates if it is connected or not as well as error state.

Our main class is called MobileMain. It owns the status bar area and is responsible for creating it. We then have a StatusBarManager class. The StatusBarManager is currently a static class, but could also be a singleton. Here is the start of the class.

public static class StatusBarManager
{
    static ScreenStatusBar StatusBar;

    /// <summary>
    /// Creates the status bar that it manages and returns it.
    /// </summary>
    public static ScreenStatusBar CreateStatusBar()
    {
        StatusBar = new ScreenStatusBar();
        return StatusBar;
    }

The MobileMain asks the StatusBarManager for a StatusBar. It then uses the StatusBar. No other classes see the StatusBar, just the StatusBarManager.

Updates to the status bar can come from pretty much anywhere in the application. There are around 20 classes that can update the text areas on the status bar and additional classes that update the icon states.

There will only every be one StatusBar and one StatusBarManager.

Any suggestions for a better implemention?

Some thoughts that I had:

Make the StatusBarManager an instance class. In my MobileMain class hold onto a static public instance of the StatusBarManager class. Then to do status bar updates you would call MobileMain.StatusBarManager.SetInformationText or some other method of the manager. The StatusBarManager would not be a singleton, but the MobileMain would only be creating a static instance of it. The issue here is that MobileMain now has a StatusBar and a StatusBarManager, which just manages the StatusBar it owns. Still also have a globally avaialble static instance to the StatusBarManager, just a different owner.

Another idea was to use something like an EventEggregator class. I've never used one, but have read about them. I guess the concept is that it would be a globally available class. In each class that wants to update the status bar it would publish a StatusBarUpdate event. The StatusBarManager would be the only classes subscribing to the StatusBarUpdate event, and receive all of the notifications. I've read though that can end up with leaks with this approach if you are not carefull with unsubscribing from events when cleaning up objects. Is this approach worth looking into?

Having a StatusBar class or a StatusBarManager class or not is not a big deal. But having many classes in your app know about StatusBars and StatusBarManagers is a bad idea, it will cause strong coupling, and some day probably pain.

How?

Imagine that the components that currently report status to a status bar have to be reused in another app that - uses a text console to report status? - reports status to multiple places? or - doesn't report status at all!

Best alternative: -Event listening. Expose a Status Changed event on your class (you can use a callback), or perhaps on an existing shared resource that your classes have in common. Other parties, like your status bar, can subscribe to the event. And should unsubscribe whenever the subscription is no longer needed/valid, to prevent leaks, as you mention!

-Since you've tagged WPF, for WPF, having a dependency property 'StatusText', might seem like another tempting option, with this approach when you have multiple status properties, you need a way of figuring out which one is telling you the most interesting status that needs to be displayed on your status bar now! Which could be a binding, multibinding (blech, complexity), or dependency property changed event handler.

However - I would advise you to keep DependencyObjects and DependencyProperties limited to your UI layer as much as possible. The reason is that they rely implicitly on a Dispatcher on the UI thread, and so can't be adapted easily for non-UI chores.

Since there are many different parts of your app you may also possibly find it's reasonable to have a combination of both of these, using some one place and some another.

Why InitializeComponent is public

9 votes

Public interface of my WPF user control contains autogenerated InitializeComponent method (which is contained in a partial class). It was a surprise for me as I expected such an internal stuff to be private.

Is there any way to remove InitializeComponent from user control public interface?

InitializeComponent is a method defined on the interface System.Windows.Markup.IComponentConnector and is used for loading the compiled page of a component.

See MSDN excerpt below from this link which has more info:

IComponentConnector is used internally by Baml2006Reader.

Implementations of InitializeComponent are widely observable as part of the infrastructure provided by frameworks or technologies that use XAML combined with application and programming models. For example, whenever you look at the generated classes for XAML root elements in WPF pages and applications, you will see InitializeComponent defined in the output. That method also exists in the compiled assembly and plays a role in the WPF application model of loading the XAML UI content at XAML parse time (and I suppose hence InitializeComponent has to be in an interface and be public so that other outside WPF related assemblies can make use of it).

To explain this further, go to the definition of InitializeComponent() method in your (say): Window1.g.cs class of say: WPFProject project, and change its access from public to private

(keep the .g.cs file open in your project otherwise the build process overrides this file, and you won't be able to see the error)

Now, when you compile your WPF project, it throws a compile error as below:

Error 22 'WPFProject.Window1' does not implement interface member 'System.Windows.Markup.IComponentConnector.InitializeComponent()'. 'WPFProject.Window1.InitializeComponent()' cannot implement an interface member because it is not public.

Additionally, InitializeComponent() is marked with the [System.Diagnostics.DebuggerNonUserCodeAttribute()] attribute so you can't step into this method while debugging.

There is another SO QA discussion, which would help you to explain more in detail

How do I disable a button bound to a current item's ICommand when there is no current item?

9 votes

Say you have a button whose command property is bound to some ICommand of the current item of some collection.

When the collection is null, the button remains enabled and clicking it seems to be a no-op. I want instead that the button remains disabled. I figured out the following to keep buttons disabled whenever the collection is null. It however seems a bit too convoluted for something that could perhaps be accomplished in a more natural, simpler, and more MVVM like.

Hence the question: is there a simpler way to keep that button disabled, ideally where no code-behind is used?

.xaml:

<Button Content="Do something" >
    <Button.Command>
        <PriorityBinding>
            <Binding Path="Items/DoSomethingCmd"  />
            <Binding Path="DisabledCmd" />
        </PriorityBinding>
    </Button.Command>
</Button>

.cs:

public class ViewModel : NotificationObject
{
    ObservableCollection<Foo> _items;

    public DelegateCommand DisabledCmd { get; private set; }

    public ObservableCollection<Foo> Items { 
        get { return _items; } 
        set { _items = value; RaisePropertyChanged("Items"); } 
    }

    public ViewModel()
    {
        DisabledCmd = new DelegateCommand(DoNothing, CantDoAnything);
    }

    void DoNothing() { }
    bool CantDoAnything()
    {
        return false;
    }
}

Edit:

A couple of notes:

  1. I am aware that I can use lambda expressions, but in this example code I do not.
  2. I know what a predicate is.
  3. I don't see how doing something with DoSomethingCmd.CanExecute will do anything to help as there is no DoSomethingCmd to access while there is no current item.
  4. So I'll re-center my question: How can I avoid using the DisabledCmd? I am not interested in moving up the DoSomethingCmd as it is not what I am looking for. I wouldn't be asking this question otherwise.

Another edit:

So I basically adopted this answer as a solution: WPF/MVVM: Disable a Button's state when the ViewModel behind the UserControl is not yet Initialized?

It is, I believe, exactly what hbarck proposes.

I'd do it similar to akjoshi, only I'd use a normal Trigger instead of a DataTrigger, and I'd check on Button.Command to be null. Since it always makes sense to disable a Button that has no Command (especially in MVVM, where there are no click eventhandlers), it would also be a good idea to include this trigger into a default style for Buttons, in order to have this behaviour on all Buttons across the application... I don't see a reason to use a dummy command.

Why does WPF not use IDisposable, and what are the ramifications?

8 votes

So, I've never done a sizable WPF project before now, but this one has me wondering; What's up with WPF classes not implementing IDisposable? By comparison, all of the UI elements in Windows Forms implement IDisposable, to assure they get rid of the underlying handles and such.

I think the same Windows objects are under the covers there, and those resources have to be released; so, how is WPF doing that?

Is there anything I need to be doing with my WPF Window objects beyond Close()ing them?

WinForms controls have handles because they are wrappers around Win32 controls. WPF controls are not (well, windows are, but the controls they host are not). The reason is that, after all, a WPF window is just a DirectX rendering context, and all controls are just a bunch of triangles. So, they don't need to be actually registered to the OS, and therefore, they don't have handles (except windows and anything that inherits from HwndHost, of course, which are Win32 objects).

That's why there's such a sizeable interop layer between WPF and WinForms: WPF controls simply aren't Windows objects.

.net localization for non-strings

8 votes

I am localizing a WPF application using .resx files. I created copies of main Resources files like Resources.en-US.resx or Resources.cs-CZ.resx. Works well for strings. However, I can't figure out how to localize other files like images or documents in resource files.

When I add a new image to Resources file (either Resources.en-US.resx or Resources.cs-CZ.resx), a copy of the file is always copied to /Resources directory. So there cannot be multiple versions of one file for multiple languages, because in one directory there can be only one file with same name.

Ideal solution would be if images from localized resources would be copied in subdirectories like /Resources/en-Us. In current conditions, I am unable to localize images and documents using .resx files. Any ideas how I can achieve this? Thank you.

The following MSDN post Resources and Localization in ASP.NET 2.0 - Displaying Localized Images states:

While ASP.NET 2.0 doesn't directly support localizing image files, it doesn't require too much custom code to achieve the desired effect.

And provides the following work around:

You can start by adding the localized versions of an image file to localized versions of a global resource file. For example, the English version of LitwareSlogan.png has been added to the global resource file named Litware.resx while the French version of LitwareSlogan.fr.png has been added to Litware.fr.resx. The resources in both resource files have been given the same name of LitwareSlogan.

Complete sample code is provided at the site.

Attached property to update style trigger on event

8 votes

I'm trying to use an attached property to trigger a style change on a UIElement when an event has fired.

Here is the case scenario:

A user sees a TextBox, and focuses then unfocuses it. Somewhere in an attached property it notices this LostFocus event and sets a property (somewhere?) to say that it HadFocus.

The style on the TextBox then knows that it should style itself differently based on this HadFocus property.

Here's how I imagine the markup to look...

<TextBox Behaviors:UIElementBehaviors.ObserveFocus="True">
<TextBox.Style>
    <Style TargetType="TextBox">
        <Style.Triggers>
            <Trigger Property="Behaviors:UIElementBehaviors.HadFocus" Value="True">
                <Setter Property="Background" Value="Pink"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</TextBox.Style>

I've tried a few combinations of the attached properties to get this working, my latest attempt throws a XamlParseException stating "Property can not be null on Trigger."

    public class UIElementBehaviors
{
    public static readonly DependencyProperty ObserveFocusProperty =
        DependencyProperty.RegisterAttached("ObserveFocus",
                                            typeof (bool),
                                            typeof (UIElementBehaviors),
                                            new UIPropertyMetadata(false, OnObserveFocusChanged));
    public static bool GetObserveFocus(DependencyObject obj)
    {
        return (bool) obj.GetValue(ObserveFocusProperty);
    }
    public static void SetObserveFocus(DependencyObject obj, bool value)
    {
        obj.SetValue(ObserveFocusProperty, value);
    }

    private static void OnObserveFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var element = d as UIElement;
        if (element == null) return;

        element.LostFocus += OnElementLostFocus;
    }
    static void OnElementLostFocus(object sender, RoutedEventArgs e)
    {
        var element = sender as UIElement;
        if (element == null) return;

        SetHadFocus(sender as DependencyObject, true);

        element.LostFocus -= OnElementLostFocus;
    }

    private static readonly DependencyPropertyKey HadFocusPropertyKey =
        DependencyProperty.RegisterAttachedReadOnly("HadFocusKey",
                                                    typeof(bool),
                                                    typeof(UIElementBehaviors),
                                                    new FrameworkPropertyMetadata(false));

    public static readonly DependencyProperty HadFocusProperty = HadFocusPropertyKey.DependencyProperty;
    public static bool GetHadFocus(DependencyObject obj)
    {
        return (bool)obj.GetValue(HadFocusProperty);
    }

    private static void SetHadFocus(DependencyObject obj, bool value)
    {
        obj.SetValue(HadFocusPropertyKey, value);
    }
}

Anyone able to direct me?

Registering a readonly dependency property does not mean to add Key to the property name. Just replace

DependencyProperty.RegisterAttachedReadOnly("HadFocusKey", ...);

by

DependencyProperty.RegisterAttachedReadOnly("HadFocus", ...);

since HadFocus is the name of the property.

Move Focus to Next Cell on Enter Key Press in WPF DataGrid?

7 votes

I want to have a Custom DataGrid which can,

  1. Move to next cell when Enter key is pressed also if it is in edit mode.
  2. When the last column in the current row is reach, the focus should move to the first cell of next row.
  3. On reaching to next cell, if the cell is editable, it should automatically became editable.
  4. If the cell contains an ComboBox not comboboxcolumn, the combobox should DropDownOpen.

Plese help me in this. I have been trying from the past few day by creating a Custom DataGrid and wrote some code in

protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)

But i failed.

public class DataGrid : System.Windows.Controls.DataGrid
{
    private void PressKey(Key key)
    {
        KeyEventArgs args = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key);
        args.RoutedEvent = Keyboard.KeyDownEvent;
        InputManager.Current.ProcessInput(args);
    }
    protected override void OnCurrentCellChanged(EventArgs e)
    {
        if (this.CurrentCell.Column != null)                
            if (this.CurrentCell.Column.DisplayIndex == 2)
            {

                if (this.CurrentCell.Item.ToString() == "--End Of List--")
                {
                    this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
                }
            }
            else if (this.CurrentCell.Column != null && this.CurrentCell.Column.DisplayIndex == this.Columns.Count() - 1)
            {
                PressKey(Key.Return);
                DataGridCell cell = DataGridHelper.GetCell(this.CurrentCell);
                int index = DataGridHelper.GetRowIndex(cell);
                DataGridRow dgrow = (DataGridRow)this.ItemContainerGenerator.ContainerFromItem(this.Items[index]);
                dgrow.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
            }
    }
    protected override void OnKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            DataGridRow rowContainer = (DataGridRow)this.ItemContainerGenerator.ContainerFromItem(this.CurrentItem);
            if (rowContainer != null)
            {
                int columnIndex = this.Columns.IndexOf(this.CurrentColumn);
                DataGridCellsPresenter presenter = UIHelper.GetVisualChild<DataGridCellsPresenter>(rowContainer);
                if (columnIndex == 0)
                {
                    DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
                    TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
                    request.Wrapped = true;
                    cell.MoveFocus(request);
                    BeginEdit();
                    PressKey(Key.Down);
                }
                else
                {
                    CommitEdit();
                    DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
                    TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
                    request.Wrapped = true;
                    cell.MoveFocus(request);
                }
                this.SelectedItem = this.CurrentItem;
                e.Handled = true;
                this.UpdateLayout();
            }
        }
    }
}

For the time beign, i have written this and its working for me.

WPF datagrid: converter and StringFormat

6 votes

I have a standard (WPF toolkit) data grid. Some of the columns (which are explicitly defined) have to be shown as percentages. Some columns have to be shown in red if the values are below 0. (The two sets of columns are not the same). I tried to implement these requirements using a StringFormat and Style, respectively. My XAML:

<Window xmlns:local="clr-namespace:myNamespace"
        xmlns:tk="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit">
    <Window.Resources>
        <local:ValueConverter x:Key="valueToForeground" />
        <Style TargetType="{x:Type tk:DataGridCell}">
            <Setter Property="Foreground"
                    Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource valueToForeground}}" />
        </Style>
    </Window.Resources>
    <Grid>
        <tk:DataGrid AutoGenerateColumns="False"
                     ItemsSource="{Binding Path=myClass/myProperty}">
            <tk:DataGrid.Columns>
                <tk:DataGridTextColumn Header="A"
                                       Binding="{Binding colA}" />
                <tk:DataGridTextColumn Header="B"
                                       Binding="{Binding colB, StringFormat=\{0:P\}}" />
                <tk:DataGridTextColumn Header="C"
                                       Binding="{Binding colC, StringFormat=\{0:P\}}" />
                <tk:DataGridTextColumn Header="D"
                                       Binding="{Binding colD, StringFormat=\{0:P\}}" />
            </tk:DataGrid.Columns>
        </tk:DataGrid>
    </Grid>
</Window>

And the relevant converter:

namespace myNamespace
{
    public class ValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            SolidColorBrush brush = new SolidColorBrush(Colors.Black);

            Double doubleValue = 0.0;
            if (value != null)
            {
                if (Double.TryParse(value.ToString(), out doubleValue))
                {
                    if (doubleValue < 0)
                        brush = new SolidColorBrush(Colors.Red);
                }
            }
            return brush;
        }

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

I think it's all pretty standard, but the problem is that the converter gets the Text value after it's gone through the StringFormat, and at that point it's difficult to parse it correctly (since in reality, not all columns have the same format). If I take out the StringFormats, the converter works fine and the text shows up in red. Am I missing something obvious? Is there an easy way to work around this? The only thing that I can think of right now is moving the formatting into a different converter, and I'm not convinced that would work.

Specify a cell style for each column as follows:

<DataGridTextColumn Header="ColA" Binding="{Binding colA, StringFormat=\{0:P\}}">
    <DataGridTextColumn.CellStyle>
        <Style TargetType="DataGridCell">
            <Setter Property="Foreground" 
                    Value="{Binding colA, Converter={StaticResource valueToForeground}}" />
         </Style>
    </DataGridTextColumn.CellStyle>
</DataGridTextColumn>

<DataGridTextColumn Header="ColB" Binding="{Binding colB, StringFormat=\{0:P\}}">
    <DataGridTextColumn.CellStyle>
        <Style TargetType="DataGridCell">
            <Setter Property="Foreground" 
                    Value="{Binding colB, Converter={StaticResource valueToForeground}}" />
         </Style>
    </DataGridTextColumn.CellStyle>
</DataGridTextColumn>

... 

and modify your converter

public class ValueConverter : IValueConverter
{
    public object Convert(
                object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((double) value < 0) ? Brushes.Red : Brushes.Black;
    }

    public object ConvertBack(
                object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}

enter image description here

How do you programmatically set focus to the SelectedItem in a WPF ListBox that already has focus?

5 votes

We want to set the SelectedItem of a ListBox programmatically and want that item to then have focus so the arrow keys work relative to that selected item. Seems simple enough.

The problem however is if the ListBox already has keyboard focus when setting SelectedItem programmatically, while it does properly update the IsSelected property on the ListBoxItem, it doesn't set keyboard focus to it, and thus, the arrow keys move relative to the previously-focused item in the list and not the newly-selected item as one would expect.

This is very confusing to the user as it makes the selection appear to jump around when using the keyboard as it snaps back to where it was before the programmatic selection took place.

Note: As I said, this only happens if you programmatically set the SelectedItem property on a ListBox that already has keyboard focus itself. If it doesn't (or if it does but you leave, then come right back), when the keyboard focus returns to the ListBox, the correct item will now have the keyboard focus as expected.

Here's some sample code showing this problem. To demo this, run the code, use the mouse to select 'Seven' in the list (thus putting the focus on the ListBox), then click the 'Test' button. Finally, tap the 'Alt' key on your keyboard to reveal the focus rect. You will see it's still actually on 'Seven' and if you use the up and down arrows, they are relative to that row, not 'Four' as a user would expect.

Note that I have Focusable set to false on the button as not to rob the listbox of focus when pressing it. If I didn't have this, the ListBox would lose focus when you click the button, and thus, when focus returned to the ListBox, it would be on the correct item.

XAML file:

<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="525" Height="350" WindowStartupLocation="CenterScreen"
    Title="MainWindow" x:Name="Root">

    <DockPanel>

        <Button Content="Test"
            DockPanel.Dock="Bottom"
            HorizontalAlignment="Left"
            Focusable="False"
            Click="Button_Click" />

        <ListBox x:Name="MainListBox" />

    </DockPanel>

</Window>

Code-behind:

using System.Collections.ObjectModel;
using System.Windows;

namespace Test
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            MainListBox.ItemsSource = new string[]{
                "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"
            };

        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MainListBox.SelectedItem = MainListBox.Items[3];
        }

    }

}

Note: Some have suggested to use IsSynchronizedWithCurrentItem, but that property synchronizes the SelectedItem of the ListBox with the Current property of the associated view. It isn't related to focus as this problem still exists.

Our work-around is to temporarily set the focus somewhere else, then set the selected item, then set the focus back to the ListBox but this has the undesireable effect of us having to make our ViewModel aware of the ListBox itself, then perform logic depending on whether or not it has the focus, etc. (i.e. you wouldn't want to simply say 'Focus elsewhere then come back here, if 'here' didn't have the focus already as you'd steal it from somewhere else.) Plus, you can't simply handle this through declarative bindings. Needless to say this is ugly.

Then again, 'ugly' ships, so there's that.

It's a couple lines of code. If you didn't want it in code-behind, I sure it could be packaged in a attached behaviour.

        private void Button_Click(object sender, RoutedEventArgs e)
    {
        MainListBox.SelectedItem = MainListBox.Items[3];
        ListBoxItem listBoxItem = (ListBoxItem) MainListBox.ItemContainerGenerator.ContainerFromItem(MainListBox.SelectedItem);
        listBoxItem.Focus();
    }

How do weak events work?

5 votes

I'm currently learning WPF and have stumbled upon the concept of weak events but I am really struggling to 'get it'. I have read countless articles on Stackoverflow and looked at code samples but it just isn't sinking in.

Here's my dilemma:

  1. I understand that when an object subscribes to an event, the source of the event has to hold a reference to the subscriber.
  2. I also understand that if the subscriber goes out of scope or is explicitly destroyed but the event source is not destroyed then the subscriber will not be garbage collected because the event source still retains a reference to the subscriber.
  3. A common method of avoiding this is to explicitly un-subscribe the subscriber from the source before the object is destroyed. I understand that this can be a problem if the programmer is not able to determine when this will occur.

So from the above I understand how the use of events can cause memory leaks and why there is a need for a weak reference pattern but what is stopping me from understanding is how does the weak event pattern actually achieve this goal? What does it do differently?

Surely even if there is a class that manages events it still has to subscribe and un-subscribe the handlers to / from the source, hence references must exist, giving the same problems with the standard way of using events.

Someone please explain to me what fundamental concept I am missing or misunderstanding and help me to 'get' the weak event pattern.

What you are missing is that Weak Events (which use Weak References under the covers, which in turn use a GCHandle) are leveraging built-in CLR behavior for the particular case of needing to access an object without holding a strong reference to it- that is, they are not constrained by the normal "rules" that your application code is subject to.

See http://sankarsan.wordpress.com/2008/08/09/weak-references/

Behind the scenes, the WeakEventManager is holding a weak reference to the event subscriber. If the subscriber happens to be GC'd before the event is raised, the WeakEventManager just shrugs and says "OK, that guy's dead, I'm just going to stop trying to notify him of this event from now on"

Is there a way to make the _ appear for hotkeys without the Alt key?

5 votes

In WPF when you make a label like this:

<Label Content="_My Label"/>

Then when you run the app and press the Alt key it will show the "M" underlined.

We have our own custom hotkey Attached Property that allows us to use Ctrl as well as Alt.

Problem is that only Alt will show the underscores.

Is there a way to show the underscore when the Ctrl key is pressed?

NOTE: I do NOT want to send a programmatic Alt KeyPress in the background when Ctrl is pressed. That will just confuse my shortcut system.

Ok! I have got a solution to show the _ for hot-keys without Alt pressed but Ctrl pressed.

Here is how to Do it :


Small Code to Press a KeyBoard Key dynamically :

//<summary>
//Function to Perform a Keyboard KeyPress.
//</summary>
void PressKey(Key KeyboardKey)
{
    KeyEventArgs args = new KeyEventArgs(Keyboard.PrimaryDevice,
    Keyboard.PrimaryDevice.ActiveSource, 0, Key.LeftAlt);
    args.RoutedEvent = Keyboard.KeyDownEvent;
    InputManager.Current.ProcessInput(args);
}

Code to Append and Remove HotKeyChar :

//<summary>
//Function to Append a HotKeyChar to a Content of a Control.
//</summary>
void AppendHotKeyChar(ContentControl Ctrl, int KeyIndex)
{
    if (Ctrl.Content.ToString().Substring(KeyIndex, 1) != "_")
    {
        Ctrl.Content = "_" + Ctrl.Content;
    }
}
//<summary>
//Function to Remove a HotKeyChar to a Content of a Control.
//</summary>
void RemoveHotKeyChar(ContentControl Ctrl, int KeyIndex)
{
    if (Ctrl.Content.ToString().Substring(KeyIndex, 1) == "_")
    {
        Ctrl.Content = Ctrl.Content.ToString().Remove(KeyIndex, 1);
    }
}

XAML Code for Button Bt1 :

<Button x:Name="Bt1" Content="Button" HorizontalAlignment="Left" Margin="169,97,0,0" VerticalAlignment="Top" Width="75"/>

Code for Window.Loaded event of the MainWindow (e.g. MainWindow1_Loaded) :

PressKey(Key.LeftAlt);

Code for Window.KeyDown event of the MainWindow (e.g. MainWindow1_KeyDown) :

if (e.Key == Key.LeftCtrl)
{
    AppendHotKey(Bt1, 0);
}

Code for Window.KeyUp event of the MainWindow (e.g. MainWindow1_KeyUp) :

if (e.Key == Key.LeftCtrl)
{
    RemoveHotKey(Bt1, 0);
}

Now, When you start your app the Alt will be pressed once dynamically.

And now every-time you press Ctrl, your Control.Content will be Appended with a _ and so the HotKey will appear underlined! But one remark is that you should create Control.Content without HotKeyChar '_' but keep an Index of where your _ will be appended.

But keep in mind that if Alt is pressed again in your app, The code will not work anymore. So, you have to press the Alt again to make the code work!

Best way to appending and removing a HotKeyChar :

  • Create an instance of List<KeyValuePair<int, Control>> to store the Index of the HotKeyChar and the Control.
  • And now in the KeyDown event just loop through the KeyValuePair<...> in the List<...>..appending the _.
  • In the KeyUp event again just loop through the KeyValuePair<...> in the List<...>..removing the _.

Hope it Helped!

Dependency property re-entrancy (or: why does this work?)

5 votes

Put simply, I can create 2 dependency properties in a WPF control and put code in each property change notification to change the other property (i.e PropA change sets PropB and PropB change sets PropA).

I would expect this to disappear up its own backside but WPF seems to handle it nicely. That's actually very handy for my purposes but I can't find this behaviour documented anywhere.

So what's going on? Does the WPF dependency property change notification system guard against reentrancy?

Representative code follows:

XAML:

<Window x:Class="WPFReentrancy1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox Text="{Binding PropB, UpdateSourceTrigger=PropertyChanged}"/>

    </Grid>
</Window>

Code behind:

 public partial class MainWindow : Window
    {

        public string PropA
        {
            get { return (string)GetValue(PropAProperty); }
            set { SetValue(PropAProperty, value); }
        }
        public static readonly DependencyProperty PropAProperty =
                        DependencyProperty.Register("PropA", typeof (string), typeof (MainWindow),new UIPropertyMetadata("0", PropAChanged));


        public string PropB
        {
            get { return (string)GetValue(PropBProperty); }
            set { SetValue(PropBProperty, value); }
        }

        public static readonly DependencyProperty PropBProperty =
            DependencyProperty.Register("PropB", typeof (string), typeof (MainWindow), new UIPropertyMetadata("", PropBChanged));

        private static void PropBChanged(DependencyObject lDependencyObject, DependencyPropertyChangedEventArgs lDependencyPropertyChangedEventArgs)
        {
            ((MainWindow) lDependencyObject).PropA = (string) lDependencyPropertyChangedEventArgs.NewValue;
        }


        private static void PropAChanged(DependencyObject lDependencyObject, DependencyPropertyChangedEventArgs lDependencyPropertyChangedEventArgs)
        {
            ((MainWindow) lDependencyObject).PropB =
                double.Parse((string) lDependencyPropertyChangedEventArgs.NewValue).ToString("0.000");
        }


        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            PropA = "1.123";
        }
    }

Those callbacks are only fired if the property changed, your code does not create an infinite loop of different values.

Try this and you will get a SO exception:

private static readonly Random _random = new Random();
private static void PropBChanged(DependencyObject lDependencyObject, DependencyPropertyChangedEventArgs lDependencyPropertyChangedEventArgs)
{
    ((MainWindow)lDependencyObject).PropA = _random.Next().ToString();
}
private static void PropAChanged(DependencyObject lDependencyObject, DependencyPropertyChangedEventArgs lDependencyPropertyChangedEventArgs)
{
    ((MainWindow)lDependencyObject).PropB = _random.Next().ToString();
}

How can I stop a dialog window from getting hidden

5 votes

If I create a class derived from System.Windows.Window and show it with ShowDialog it appears above the main window as expected, and the main window is disabled. However it is possible to put both windows behind other applications, and then just bring the main window back. This just leaves a single window which appears to have crashed, and can be confusing.

Is it possible to ensure that the dialog window is always displayed if the main window is shown? The MessageBox.Show dialog has no such problems

Update:

A test dialog is defined as

public partial class MyDialog : Window
{
    public MyDialog()
    {
        InitializeComponent();
    }
}

and called using

    MyDialog d = new MyDialog();
    d.ShowDialog();

you have to set the Owner property.

MyDialog d = new MyDialog();
d.Owner = Application.Current.MainWindow;//or your owning window
d.ShowDialog();

Why ConverterParameter is not bindable in wpf and Silverlight?

4 votes

I am learning wpf for a couple of weeks and I am surprised that WPF / Silverlight has the powerful binding feature.

I am also surprised that some properties do not support binding e.g converterparameter. I found that this one is a primitive datatype so we can't bind it with other sources.

I would like to find out why these kind of properties are not bindable?

Thanks in Advance.

It is not bindable because IValueConverters are not part of the logical tree.
Therefore, they don't inherit the DataContext that makes DataBinding possible.

You can find several tricks on how to make such an object inherit the DataContext, but I would recommand against it: it's hackish, and in the end it will harm the readability of your code (nobody expects bound stuff in an IValueConverter).