Best wpf questions in June 2012

Custom search tab in Windows start menu search with C#

22 votes

I am interested to know how can I do the same thing that the apllication listed below does: Start Menu Calculator

I want to know how can I create an custom tab in Start Menu Search and then handle it with my WPF application. It should only be enabled until my application is running.( Just like what The calculator does )

I read something about windows API Code Pack and I downloaded it but I don't know how can I use it. I searched through it but I didn't find anything.( If you know how I could do this using with Windows API Code Pack, please write an example that explains how to do it in C#)

  • The main exe "Start Menu Calculator.exe" installs a windows hook (using SetWindowsHookEx) into explorer.exe. The hook is implemented as usual in SBLib.dll which is then injected into Windows Explorer's memory space.

  • This hook searches for window handles belonging to the search box. See a discussion around this here: How do I get a "handle" on the Windows Search textbox? and probably sub classes the search box windows (if you kill the "Start Menu Calculator.exe" process abruptly, it crashes Windows Explorer too... which kinda confirms this)

  • It then reacts to key presses, and I suppose it butchers up the result window. In the hierarchies of Windows, I think it's a Window named "Desktop Search Open View", you can get to it with SPY++ under "Start Menu", aside the windows mentioned in the msdn forum above.

So, no nice API behind this nice application. Massive hacks instead :-)

I think however, some level of integration is possible, using documented behavior, with the search box. I have not dug further, but there is the notion of federated search in Windows (Windows 7 Federated Search). I don't see if this would be capable of reacting instantaneously to what the user types in though...

As a side note, if you're also looking for a way to run javascript code from C#, there is a question here on SO that says it all: parse and execute JS by C#

WPF bing maps control polylines/polygons not draw on first add to collection

10 votes

I'm working on this surface project where we have a bing maps control and where we would like to draw polylines on the map, by using databinding.

The strange behaviour that's occuring is that when I click the Add button, nothing happens on the map. If I move the map little bit, the polyline is drawn on the map. Another scenario that kind of works, is click the add button once, nothing happens, click it again both polylines are drawn. (In my manual collection I have 4 LocationCollections) so the same happens for the 3rd click and the fourth click where again both lines are drawn.

I have totally no idea where to look anymore to fix this. I have tried subscribing to the Layoutupdated events, which occur in both cases. Also added a collectionchanged event to the observablecollection to see if the add is triggered, and yes it is triggered. Another thing I tried is changing the polyline to pushpin and take the first location from the collection of locations in the pipelineviewmodel, than it's working a expected.

I have uploaded a sample project for if you want to see yourself what's happening.

Really hope that someone can point me in the right direction, because i don't have a clue anymore.

Below you find the code that i have written:

I have the following viewmodels:

MainViewModel

public class MainViewModel
{
    private ObservableCollection<PipelineViewModel> _pipelines;

    public ObservableCollection<PipelineViewModel> Pipes
    {
        get { return _pipelines; }
    }

    public MainViewModel()
    {
        _pipelines = new ObservableCollection<PipelineViewModel>();
    }
}

And the PipelineViewModel which has the collection of Locations which implements INotifyPropertyChanged:

PipelineViewModel

public class PipelineViewModel : ViewModelBase
{
    private LocationCollection _locations;

    public string Geometry { get; set; }
    public string Label { get; set; }
    public LocationCollection Locations
    {
        get { return _locations; }
        set
        {
            _locations = value;
            RaisePropertyChanged("Locations");
        }
    }
}

My XAML looks like below:

<s:SurfaceWindow x:Class="SurfaceApplication3.SurfaceWindow1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF" 
    Title="SurfaceApplication3">
    <s:SurfaceWindow.Resources>
        <DataTemplate x:Key="Poly">
            <m:MapPolyline Locations="{Binding Locations}" Stroke="Black" StrokeThickness="5" />
        </DataTemplate>
    </s:SurfaceWindow.Resources>
  <Grid>
        <m:Map ZoomLevel="8" Center="52.332074,5.542302" Name="Map">
            <m:MapItemsControl Name="x" ItemsSource="{Binding Pipes}" ItemTemplate="{StaticResource Poly}" />
        </m:Map>
        <Button Name="add" Width="100" Height="50" Content="Add" Click="add_Click"></Button>
    </Grid>
</s:SurfaceWindow>

And in our codebehind we are setting up the binding and the click event like this:

private int _counter = 0;
private string[] geoLines;

private MainViewModel _mainViewModel = new MainViewModel();

/// <summary>
/// Default constructor.
/// </summary>
public SurfaceWindow1()
{
    InitializeComponent();

    // Add handlers for window availability events
    AddWindowAvailabilityHandlers();

    this.DataContext = _mainViewModel;

    geoLines = new string[4]{ "52.588032,5.979309; 52.491143,6.020508; 52.397391,5.929871; 52.269838,5.957336; 52.224435,5.696411; 52.071065,5.740356",
                                "52.539614,4.902649; 52.429222,4.801025; 52.308479,4.86145; 52.246301,4.669189; 52.217704,4.836731; 52.313516,5.048218",
                                "51.840869,4.394531; 51.8731,4.866943; 51.99841,5.122375; 52.178985,5.438232; 51.8731,5.701904; 52.071065,6.421509",
                                "51.633362,4.111633; 51.923943,6.193542; 52.561325,5.28717; 52.561325,6.25946; 51.524125,5.427246; 51.937492,5.28717" };
}

private void add_Click(object sender, RoutedEventArgs e)
{
    PipelineViewModel plv = new PipelineViewModel();
    plv.Locations = AddLinestring(geoLines[_counter]);
    plv.Geometry = geoLines[_counter];

    _mainViewModel.Pipes.Add(plv);

    _counter++;
}

private LocationCollection AddLinestring(string shapegeo)
{
    LocationCollection shapeCollection = new LocationCollection();

    string[] lines = Regex.Split(shapegeo, ";");
    foreach (string line in lines)
    {
        string[] pts = Regex.Split(line, ",");

        double lon = double.Parse(pts[1], new CultureInfo("en-GB"));
        double lat = double.Parse(pts[0], new CultureInfo("en-GB"));
        shapeCollection.Add(new Location(lat, lon));
    }

    return shapeCollection;
}

I did some digging on this problem and found that there is a bug in the Map implementation. I also made a workaround for it which can be used like this

<m:Map ...>
    <m:MapItemsControl Name="x"
                       behaviors:MapFixBehavior.FixUpdate="True"/>
</m:Map>

I included this fix in your sample application and uploaded it here: SurfaceApplication3.zip


The visual tree for each ContentPresenter looks like this

enter image description here

When you add a new item to the collection the Polygon gets the wrong Points initially. Instead of values like 59, 29 it gets something like 0.0009, 0.00044.

The points are calculated in MeasureOverride in MapShapeBase and the part that does the calculation looks like this

MapMath.TryLocationToViewportPoint(ref this._NormalizedMercatorToViewport, location, out point2);

Initially, _NormalizedMercatorToViewport will have its default values (everything is set to 0) so the calculations goes all wrong. _NormalizedMercatorToViewport gets set in the method SetView which is called from MeasureOverride in MapLayer.

MeasureOverride in MapLayer has the following two if statements.

if ((element is ContentPresenter) && (VisualTreeHelper.GetChildrenCount(element) > 0))
{
    child.SetView(...)
}

This comes out as false because the ContentPresenter hasn't got a visual child yet, it is still being generated. This is the problem.

The second one looks like this

IProjectable projectable2 = element as IProjectable;
if (projectable2 != null)
{
    projectable2.SetView(...);
}

This comes out as false as well because the element, which is a ContentPresenter, doesn't implement IProjectable. This is implemented by the child MapShapeBase and once again, this child hasn't been generated yet.

So, SetView never gets called and _NormalizedMercatorToViewport in MapShapeBase will have its default values and the calculations goes wrong the first time when you add a new item.


Workaround

To workaround this problem we need to force a re-measure of the MapLayer. This has to be done when a new ContentPresenter is added to the MapItemsControl but after the ContentPresenter has a visual child.

One way to force an update is to create an attached property which has the metadata-flags AffectsRender, AffectsArrange and AffectsMeasure set to true. Then we just change the value of this property everytime we want to do the update.

Here is an attached behavior which does this. Use it like this

<m:Map ...>
    <m:MapItemsControl Name="x"
                       behaviors:MapFixBehavior.FixUpdate="True"/>
</m:Map>

MapFixBehavior

public class MapFixBehavior
{
    public static DependencyProperty FixUpdateProperty =
        DependencyProperty.RegisterAttached("FixUpdate",
                                            typeof(bool),
                                            typeof(MapFixBehavior),
                                            new FrameworkPropertyMetadata(false,
                                                                          OnFixUpdateChanged));

    public static bool GetFixUpdate(DependencyObject mapItemsControl)
    {
        return (bool)mapItemsControl.GetValue(FixUpdateProperty);
    }
    public static void SetFixUpdate(DependencyObject mapItemsControl, bool value)
    {
        mapItemsControl.SetValue(FixUpdateProperty, value);
    }

    private static void OnFixUpdateChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        MapItemsControl mapItemsControl = target as MapItemsControl;
        ItemsChangedEventHandler itemsChangedEventHandler = null;
        itemsChangedEventHandler = (object sender, ItemsChangedEventArgs ea) =>
        {
            if (ea.Action == NotifyCollectionChangedAction.Add)
            {
                EventHandler statusChanged = null;
                statusChanged = new EventHandler(delegate
                {
                    if (mapItemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                    {
                        mapItemsControl.ItemContainerGenerator.StatusChanged -= statusChanged;
                        int index = ea.Position.Index + ea.Position.Offset;
                        ContentPresenter contentPresenter =
                            mapItemsControl.ItemContainerGenerator.ContainerFromIndex(index) as ContentPresenter;
                        if (VisualTreeHelper.GetChildrenCount(contentPresenter) == 1)
                        {
                            MapLayer mapLayer = GetVisualParent<MapLayer>(mapItemsControl);
                            mapLayer.ForceMeasure();
                        }
                        else
                        {
                            EventHandler layoutUpdated = null;
                            layoutUpdated = new EventHandler(delegate
                            {
                                if (VisualTreeHelper.GetChildrenCount(contentPresenter) == 1)
                                {
                                    contentPresenter.LayoutUpdated -= layoutUpdated;
                                    MapLayer mapLayer = GetVisualParent<MapLayer>(mapItemsControl);
                                    mapLayer.ForceMeasure();
                                }
                            });
                            contentPresenter.LayoutUpdated += layoutUpdated;
                        }
                    }
                });
                mapItemsControl.ItemContainerGenerator.StatusChanged += statusChanged;
            }
        };
        mapItemsControl.ItemContainerGenerator.ItemsChanged += itemsChangedEventHandler;
    }

    private static T GetVisualParent<T>(object childObject) where T : Visual
    {
        DependencyObject child = childObject as DependencyObject;
        while ((child != null) && !(child is T))
        {
            child = VisualTreeHelper.GetParent(child);
        }
        return child as T;
    }
}

MapLayerExtensions

public static class MapLayerExtensions
{
    private static DependencyProperty ForceMeasureProperty =
        DependencyProperty.RegisterAttached("ForceMeasure",
                                            typeof(int),
                                            typeof(MapLayerExtensions),
                                            new FrameworkPropertyMetadata(0,
                                                FrameworkPropertyMetadataOptions.AffectsRender |
                                                FrameworkPropertyMetadataOptions.AffectsArrange |
                                                FrameworkPropertyMetadataOptions.AffectsMeasure));

    private static int GetForceMeasure(DependencyObject mapLayer)
    {
        return (int)mapLayer.GetValue(ForceMeasureProperty);
    }
    private static void SetForceMeasure(DependencyObject mapLayer, int value)
    {
        mapLayer.SetValue(ForceMeasureProperty, value);
    }

    public static void ForceMeasure(this MapLayer mapLayer)
    {
        SetForceMeasure(mapLayer, GetForceMeasure(mapLayer) + 1);
    }
}

Why are many properties in WPF an 'Object' instead of an interface?

10 votes

I may be missing something about the fundamentals of WPF design, but I was wondering why many properties on WPF controls are exposed as the type 'Object'?

For example, MenuItem.Icon is an Object, and so is MenuItem.ToolTip. As a near first time user, this was very confusing to me (it felt like I was using a dynamic programming language, having no idea whether setting ToolTip to a String type would even work or not). Moreover, I tried to set the Icon to a 'System.Drawing.Icon' and I get an ArgumentException of "Argument 'picture' must be a picture that can be used as a Icon." Shouldn't the property be typed so it can at least describe what in the world you're supposed to give it?

Honestly, my guess as to the reason is because you cannot implement an interface on a type you did not create (without creating a wrapper), but that's just a guess.

Thanks very much for your answers and insights!

The main reason in my opinion is that since an Object is the "ultimate base class of all classes in the .Net Framework". This gives you flexibility, in WPF you are not limited to a predefined type. Wpf is different and has a learning curve, but it does give you a lot more options to create a product that looks good.

i.e.

You can assign a TextBox to a ToolTip:

TextBox tb = new TextBox();
tb.Text = "Hello World";
this.ToolTip = tb;

a Bitmap

BitmapImage myBitmapImage = new BitmapImage(new Uri((@"C:\Temp\20100706.jpg")));
Image image = new Image();
image.Source = myBitmapImage;
this.ToolTip = image;

and assigning a Image to a MenuItem

BitmapImage myBitmapImage = new BitmapImage(new Uri((@"C:\Temp\20100706.jpg")));
Image image = new Image();
image.Source = myBitmapImage;
menuItem1.Icon = image;

1.2GB memory exception

8 votes

I read about memory limit

I have an application which works with huge images which needs to be streamed. Like in a video processing with single frames. The application has about 40 plugins, each of them can contain database, image processing and WPF GUI.

The application also has 2 plugins which uses older DotNet Winforms.

All works well except the application goes over about 1.2GB in RAM. Then on unusual locations in the plugins where new memory is allocated I receive the "Out of Memory exception".

I am working on a 64Bit system compiled as 32Bit. I have no more idea what to do and how to search for any fault.

Is there a limit or can I catch them?

It is very difficult to write a 32-bit program that consumes all of the available virtual memory space. You'll hit the wall well below 2 gigabytes, what you run out of first is a chunk of virtual memory that's large enough to fit the requested size. You can only get up to the 2GB limit by making small allocations, small enough to fit in the holes.

That wall hits early in a program that manipulates bitmaps. They can consume a big chunk of VM to store the bitmap pixels and it needs to be a contiguous allocation. They are stored in an array, not a tree. It's an unmanaged memory allocation, typical .NET memory profilers tend to be a bit helpless to show you the problem.

There isn't anything reasonable you can do about address space fragmentation, the notion that consuming all available VM should be possible is just wrong. You can get more breathing space on a 64-bit operating system by running editbin.exe in a post build event and use its /LARGEADDRESSAWARE command line option. That allows the process to use the available 4 gigabytes of VM, an option that's specific to the 64-bit version of Windows and possible because Windows doesn't need the upper 2GB. And of course, changing the platform target to AnyCPU is a quick and easy way to get gobs of virtual memory.

Undefined CLR namespace - No solution found

8 votes

I've been researching for a while now trying to find a reason why the following would be occuring, but no solutions on StackOverflow or Google are able to help me.

I have a custom UserControl that is attempting to reference a namespace within the same project:

xmlns:my="clr-namespace:ColorPicker"

however when I compile I get the following error:

Undefined CLR namespace. The 'clr-namespace' URI refers to a namespace 'ColorPicker' that is not included in the assembly.

This is resulting in not being able to build my project or reference other custom controls within the xaml, generating these kinds of errors:

The type 'my:ColorSelector' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.

I've attempted all the solutions given in these posts:

adding a custom namespace to xaml

WPF xmlns: The 'clr-namespace' URI refers to a namespace that is not included in the assembly

The 'clr-namespace' URI refers to a namespace that is not included in the assembly

Undefined CLR namespace

Also, just to be clear, I'm not getting any other errors about other files in this project, so it doesn't seem like it could be the result of other files not compiling.

UPDATE: A sample project that produces the error for me can be downloaded here: http://www.filefactory.com/file/28fbmhj3f4qj/n/ColorPicker_zip

Your first linked question has the answer. The answer is: you have to build the assembly containing the namespace and referenced classes/controls before you can reference it in .xaml. I commented out your xaml namespace declarations, then commented out the xaml elements from those namespaces, then commented out the C# code that broke as a result of those elements no longer being declared. In other words, I kept commenting till I could build successfully. Once the assembly built, I uncommented the xaml namespace declarations, then the elements. This gave an error about needing to use x:Name instead of Name on those elements, so I did so. Then uncommented the C# code and it builds.

Why WPF binding handles INotifyPropertyChanged in two different ways?

7 votes

I recently find out that wpf handles INotifyPropertyChanged in two different ways. I just wanna know what's the reason.

Let us take a normal twoway binding with validation true.

if you set a property from ui to viewmodel it goes like this.

  • setter call started
  • value set
  • INotifyPropertyChanged started
  • INotifyPropertyChanged done
  • setter done
  • getter called and done
  • IDataErrorInfo called and done

but if you set the property in your viewmodel it goes like this

  • setter call started
  • value set
  • INotifyPropertyChanged started
  • getter called and done
  • IDataErrorInfo called and done
  • INotifyPropertyChanged done
  • setter done

Changing property from UI to ViewModel may lead to deadlock kind of situation which might run into end less recursive calls in two way scenarios. In order to block this from happening, when WPF is making change to model, it will continue to track changes via INotifyPropertyChanged ,but this change will be queued in dispatcher queue, and it will be executed after its current update is finished.

Since the change in viewmodel is not initiated by WPF, WPF will not queue the operation, it will immediately execute the change.

Why I get squares instead of text?

7 votes

I tried to install and use a font in my WPF application, but all I get is like this: enter image description here

Here is the code I tried to use the font:

richtext1.FontFamily = "SH_Roq'a";

The expected result is: ( snap shot from MS Word ) enter image description here

If I try to add the font file to the project folder, and use it as a resource, like this:

richtext1.FontFamily = "./#SH_Roq'a";

I won't get the square results, but, I won't have the expected font either! What I get is Tahoma font:

enter image description here

which is not the targeted font, please download the targeted font file here for experiments

Any help is appreciated!

EDIT

The plain text for the above captured text is:

تفاح احمر

So for those who are experts with using fonts, they can experiment with.

There are several problems with the font:

  1. it is not Unicode-encoded: it uses a Platform ID 3, Encoding ID 0 cmap subtable, also known as a "Symbol" table. These do not generally work well outside of old-style Windows desktop applications.

  2. there are no OpenType Layout features (GSUB, GPOS, etc.) which are essential for correct shaping (initial, medial, final, isolated forms; lam-alef ligatures, etc.) of Arabic/Urdu/Farsi writing.

  3. it uses a very, very old OS/2 table (version 0). Some Microsoft technologies require version 1 at minimum. There are also reserved bits set in the fsSelection field of this table and a number of other suspect settings.

I stopped looking there. This font appears to have been built with very old tools, using outdated knowledge of font construction. It needs significant updating in order to work correctly in modern environments.

Displaying Entities in TreeView using MVVM

6 votes

I am making a WPF application following MVVM pattern. In this i am using entity framework,

my entity structure is simple, it has 3 entities: department, course, books,

a department can have many courses, and a course can have many books,

now i want to show this in a treeview, so my output in wpf should look like this,

Department1

  Course1

    Book1

    Book2

  Course2

    Book3

Department2

  Course

     Book

Department3   

in my ViewModel i have EntityContext object. But i dont know how to show this in a treeview. how i can do this.

I prepared the small sample to replicate this..

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:this="clr-namespace:TestApp"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <this:TreeViewModel />
    </Window.DataContext>

    <Window.Resources>

        <HierarchicalDataTemplate ItemsSource="{Binding Courses}" DataType="{x:Type this:Department}">
            <Label Content="{Binding DepartmentName}"/>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate ItemsSource="{Binding Books}" DataType="{x:Type this:Course}">
            <Label Content="{Binding CourseName}"/>
        </HierarchicalDataTemplate>

        <DataTemplate DataType="{x:Type this:Book}">
            <Label Content="{Binding BookName}"/>
        </DataTemplate>

    </Window.Resources>

    <Grid>
        <TreeView ItemsSource="{Binding Departments}">

        </TreeView>
    </Grid>
</Window>

Model and ViewModel classes.

public class Book :ViewModelBase
    {
        private string bookname = string.Empty;

        public string BookName
        {
            get
            {
                return bookname;
            }
            set
            {
                bookname = value;
                OnPropertyChanged("BookName");
            }
        }

        public Book(string bookname)
        {
            BookName = bookname;
        }
    }

Department class

public class Department : ViewModelBase
    {
        private List<Course> courses;

        public Department(string depname)
        {
            DepartmentName = depname;
            Courses = new List<Course>()
            {
                new Course("Course1"),
                new Course("Course2")
            };
        }

        public List<Course> Courses
        {
            get
            {
                return courses;
            }
            set
            {
                courses = value;
                OnPropertyChanged("Courses");
            }
        }

        public string DepartmentName
        {
            get;
            set;
        }
    }

Course class

public class Course :ViewModelBase
    {
        private List<Book> books;

        public Course(string coursename)
        {
            CourseName = coursename;
            Books = new List<Book>()
            {
                new Book("JJJJ"),
                new Book("KKKK"),
                new Book("OOOOO")
            };
        }

        public List<Book> Books
        {
            get
            {
                return books;
            }
            set
            {
                books = value;
                OnPropertyChanged("Books");
            }
        }

        public string CourseName
        {
            get;
            set;
        }
    }

TreeViewModel class.

public class TreeViewModel :ViewModelBase
    {
        private List<Department> departments;

        public TreeViewModel()
        {
            Departments = new List<Department>()
            {
                new Department("Department1"),
                new Department("Department2")
            };
        }

        public List<Department> Departments
        {
            get
            {
                return departments;
            }
            set
            {
                departments = value;
                OnPropertyChanged("Departments");
            }
        }
    }

ViewModelBase class.

public class ViewModelBase :INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propname));
            }
        }
    }

Finally it displays the data in the hierarchical format.. I hope this would satisfy you...

Displaying many-to-many groupings in WPF

6 votes

I'm not sure if i'm on the right track with this one, but essentially I'm trying to display a grouped list of items, where each item can be a member of multiple groups. i.e. The two entities are related on a many-to-many basis. I'll try to explain...


enter image description here


enter image description here


My question is, how do I group items like this into a control? I'm aware of the ICollectionView and the PropertyGroupDescription, but that doesn't seem to serve my purpose here (it seems to only work in a one-to-many scenario).

Any ideas?

Some points to note:

  1. When displayed in a list, I want to show all Component's grouped by Kit.
  2. When I select a component from this list, I only want that particular instance of the Component (i.e. I want Component + the Kit I selected it from).
  3. Ignore the fact that i'm using a TreeView to show the items below, as i'll actually be using a ListBox with a GroupStyle
  4. I'm using code-first EF 4.3.1 and the WPF MVVM pattern.

Use the Include() method on the context object to include the related entities.

Create a proxy for a dependency property

6 votes

I am trying to create a simple dependency property proxy. I made a custom control, it's a file picker, which is made off a textbox (name: "TextBox_FilePath") and a button showing the open file dialog.

As I am making a reusable control I'd like it to have a "SelectedFilePath" property. As the Text property seems to be perfect for my control to be the "SelectedFilePath" property, I'd just like to proxy these dependency property.

The first approach I made was:

public static readonly DependencyProperty SelectedFilePathProperty = TextBox.TextProperty;

public string SelectedFilePath
{
    get { return (string) this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
    set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}

which worked, but throwed an exception when trying to bind to that property. Then I came off with:

public static readonly DependencyProperty SelectedFilePathProperty =
    DependencyProperty.Register("SelectedFilePath", typeof (string), typeof (FilePicker), new PropertyMetadata(default(string)));

public string SelectedFilePath
{
    get { return (string) this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
    set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}

which does work, but I've got no idea why?! Where did I specify I wanted the text property of the textbox?

What am I missing to simply proxy out that dependency property?

EDIT: The solution with AddOwner doesn't work too, it throws an Excetion saying "binding can only be applied on a dependency property". Code:

public static readonly DependencyProperty SelectedFilePathProperty =
    TextBox.TextProperty.AddOwner(typeof(FilePicker));

public string SelectedFilePath
{
    get { return (string)this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
    set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}

What don't I understand?

EDIT2: For everybody else having issues understanding the answer, I've made a little graphic

The first approach does not work because the property is registered only for the TextBox, adding a reference in another class does nothing.

The second one just creates a whole new string property.

If you really want to reuse the TextBox.TextProperty call AddOwner on it.

e.g.

public static readonly DependencyProperty SelectedFilePathProperty =
    TextBox.TextProperty.AddOwner(typeof(FilePicker));

(Note that this property is registered as "Text", so you probably should just create a new property with the name you want as you did already. I would also recommend to set metadata flags to bind two-way by default if you want to have the same binding behaviour as TextBox.Text.)

ContentControl is not visible when application starts via UI Automation test, but its visible when application starts by user

6 votes

We are using the prism and WPF to build application. Recently we started using UI Automation (UIA) to test our app. But some strange behavior occurred when we run UIA test. Here's simplified shell:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>    
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <TextBlock 
        Grid.Row="0" Grid.Column="0"
        Name="loadingProgressText"
        VerticalAlignment="Center" HorizontalAlignment="Center"
        Text="Loading, please wait..."/>

    <Border
        Grid.Row="0" 
        x:Name="MainViewArea">
        <Grid>
            ...
        </Grid>
    </Border>

    <!-- Popup -->
    <ContentControl 
        x:Name="PopupContentControl"
        Grid.Row="0" 
        prism:RegionManager.RegionName="PopupRegion"
        Focusable="False">
    </ContentControl>

    <!-- ErrorPopup -->
    <ContentControl 
        x:Name="ErrorContentControl"
        Grid.Row="0" 
        prism:RegionManager.RegionName="ErrorRegion"
        Focusable="False">
    </ContentControl>
</Grid>

In our app, we use layers (Popup and ErrorPopup) to hide MainViewArea, to deny access to the controls. To show Popup, we use next method:

    //In constructor of current ViewModel we store _popupRegion instance to the local variable:
    _popupRegion = _regionManager.Regions["PopupRegion"];
    //---

    private readonly Stack<UserControl> _popups = new Stack<UserControl>();
    public void ShowPopup(UserControl popup)
    {
        _popups.Push(popup);

        _popupRegion.Add(PopupView);
        _popupRegion.Activate(PopupView);
    }

    public UserControl PopupView
    {
        get
        {
            if (_popups.Any())
                return _popups.Peek();
            return null;
        }
    }

Similar to this, we show ErrorPopup over all elements of our application:

    // In constructor we store _errorRegion:
    _errorRegion = _regionManager.Regions["ErrorRegion"]
    // --- 

    private UserControl _error_popup;

    public void ShowError(UserControl popup)
    {
        if (_error_popup == null)
        {
            _error_popup = popup;
            _errorRegion.Add(_error_popup);
            _errorRegion.Activate(_error_popup);
        }
    }

Mistics...

When we run it as users do it (double click on app icon), we can see both custom controls (using AutomationElement.FindFirst method, or through Visual UI Automation Verify). But when we start it using UI Automation test - ErrorPopup disapears from the tree of the controls. We trying to start the application like this:

System.Diagnostics.Process.Start(pathToExeFile);

I think that we missed something. But what?

Edit #1

As @chrismead said, we tried to run our app with UseShellExecute flag set to true, but this does not help. But if we start app from cmd line, and manually click the button, Popup and ErrorPopup are visible in automation controls tree.

    Thread appThread = new Thread(delegate()
        {
            _userAppProcess = new Process();
            _userAppProcess.StartInfo.FileName = pathToExeFile;
            _userAppProcess.StartInfo.WorkingDirectory = System.IO.Directory.GetCurrentDirectory();
            _userAppProcess.StartInfo.UseShellExecute = true;
            _userAppProcess.Start();

        });
        appThread.SetApartmentState(ApartmentState.STA);
        appThread.Start();

One of our suggestion is when we use method FindAll or FindFirst to search the button to click, window somehow cached its UI Automation state, and does not update it.

Edit #2 We have find, that extension method of prism library IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) have some strange behavior. If we stopped use it, this solve our problem particulary. Now we able to see ErrorView and any kind of view in PopupContentControl, and application updates UIA elements tree structure. But this is not an answer - "Just stop use this feature"!

In MainViewArea we have a ContentControl, which updates it content depending on user actions, and we are able to see only the first loaded UserControl to that ContentControl.Content property. This is performed like this:

IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.MainContentRegion, this.Uri);

And if we change the view, no updates will performed in UI Automation tree - the first loaded view will be in it instead. But visually we observe another View, and WPFInspector shows it properly (its show not a UI Automation tree), but Inspect.exe - not.

Also our suggestion that window use some kind of caching is wrong - caching in UI Automation client we have to turn on explicitly, but we don't do it.

I'm sorry that I've missed some detail, that was the key to the answer. I think that it was not important thing. Anyway.

We used NavBar from DevExpress controls library for WPF. What turns out, is when NavBar is present, dynamically created views are not appears on the UI Automation tree. When remove it from the window, there was an ability to see all dynamically loaded views. What does the NavBar - still mistic for me.

Here bright example to see what happened, if NavBar is present or absent on the Window (DevExpress is required).

MainWindow.xaml:

<Window xmlns:dxn="http://schemas.devexpress.com/winfx/2008/xaml/navbar"
        x:Class="Test.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 Name="ContentGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <!--Comment NavBar to see dynamic control in UI Automation tree-->
        <dxn:NavBarControl Name="asdasd">
            <dxn:NavBarControl.Groups>
                <dxn:NavBarGroup Header="asdasdasdasd" />
            </dxn:NavBarControl.Groups>
        </dxn:NavBarControl>
        <TextBox Grid.Column="1" Name="Statictb" Text="static is visible in ui automation tree" />
        <Button Grid.Row="1" Content="Create controls" Height="25"  Click="Button_Click"/>
    </Grid>
</Window>

MainWindow.xaml.cs

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

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        TextBox tb = new TextBox();
        Grid.SetRow(tb, 1);
        Grid.SetColumn(tb, 1);
        tb.Text = "dynamic is not visible, if NavBar here...";
        ContentGrid.Children.Add(tb);
    }
}

Edit

According to the DevExpress answer on their support site:

After a peer is created, listening of automation events may cause performance issues. We have decided to clear invocation lists of automation events to resolve it. In your specific situation, you need to disabling clearing. To do it, please set the static DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled property to False in the Window constructor.

This solve the problem.

Dictionaries in C#: Changing the Key of a Dictionary

6 votes

I am very new to dictionaries. Very new meaning that I started using them about 6 hours ago :p. Anyways, I want to know if there is a way to change the key of a dictionary.

Here is my dictionary:

 Dictionary<string, string> Information = new Dictionary<string, string>();

Here is how I am adding to the Dictionary(This is fired every time the user enters info and hits a button:

Information.Add(txtObjectNumber.Text, addressCombined);

The user needs to be able to edit both fields as well as remove the whole record.

So pretty much the application needs to add 'txtNumber' and 'txtComments' where 'txtNumber' = 'txtObjectNumber".

Thank you for your help.

The key is the mechanism that will allow you to find the data (the "value") later.

For example, if you did

information.Add("Kris", "Vandermotten");

you'd be able to find "Vandermotten" back later if you know "Kris".

Now in that context, what does it mean to change "Kris"? You put data in under the name "Kris" and want to get it back out searching for "Bob"? You won't find it.

In a way, dictionary key's are very much like primary keys in a relational database. The refer to the logical identity of the value. So for one thing, they should be uniquely identifying it.

So maybe this example doesn't make sense. Maybe something like

information.Add(42, new Person("Kris", "Vandermotten")

makes more sense. The question then of course is: what's the 42? Sometimes there is a natural candidate for such a key, like an employee number or something, sometimes there isn't.

When there is none, maybe you need to do

List<Person> information = new List<Person>();

information.Add(new Person("Kris", "Vandermotten"));

And of course, if a Person object allows changing the first name property, and that's what you want to do, then do it. But "changing dictionary keys" doesn't make a lot of sense to me.

How to view the values stored in a list in C#

6 votes

I am trying to learn how to use lists in C#. There are a lot of tutorials out there, but none of them really explain how to view a list that contains a record.

Here is my code:

class ObjectProperties
{
    public string ObjectNumber { get; set; }
    public string ObjectComments { get; set; }
    public string ObjectAddress { get; set; }
}

List<ObjectProperties> Properties = new List<ObjectProperties>();
ObjectProperties record = new ObjectProperties
    {
        ObjectNumber = txtObjectNumber.Text,
        ObjectComments = txtComments.Text,
        ObjectAddress = addressCombined,
    };
Properties.Add(record);

I want to display the values in a messagebox. Right now I am just making sure the information is going into the list. I also want to learn how to find a value in the list and get the other information that is related to it, such as, I want to find the item by the Object Number and if it is in the list then it will return the address. I am also using WPF, if that makes a difference. Any help will be appreciated. Thank You.

The best way is to override ToString in your class and use string.Join to join all your records:

var recordsAsString = string.Join(Environment.NewLine, 
            Properties.Select(p => p.ToString()));
MessagBox.Show(recordsAsString);

Here's a possible implementation of ToString:

class ObjectProperties
{
    public string ObjectNumber { get; set; }
    public string ObjectComments { get; set; }
    public string ObjectAddress { get; set; }

    public override string ToString() 
    {
        return "ObjectNumber: " 
              + ObjectNumber 
              + " ObjectComments: " 
              + ObjectComments 
              + " ObjectAddress: " 
              + ObjectAddress;
    }
}

I also want to learn how to find a value in the list and get the other information that is related to it, such as, I want to find the item by the Object Number and if it is in the list then it will return the address.

There are several ways to search a List<T>, here are two:

String numberToFind = "1234";
String addressToFind = null;
// using List<T>.Find method
ObjectProperties obj = Properties.Find(p => p.ObjectNumber == numberToFind);
//using Enumerable.FirstOrDefault method (add using System.Linq)
obj = Properties.FirstOrDefault(p => p.ObjectNumber == numberToFind);
if (obj != null)
    addressToFind = obj.ObjectAddress;

How to get ToolTip binding to work with a ComboBox?

5 votes

Currently I have a ComboBox defined as:

<ComboBox Name="comboItems" ItemsSource="{Binding Path=EnumDataItems}"
            DisplayMemberPath="Description" 
            ToolTip="{Binding Path=ToolTip}" // never displays the value
            SelectedValuePath="Value" SelectedValue="{Binding Path=Value}" />

Everything works except the ToolTip. The property that it should bind to; ToolTip does contain a value. I'm sure of this because when I do the following, I see a result confirming that ToolTip contains a value:

<ComboBox Name="comboItems" ItemsSource="{Binding Path=EnumDataItems}" 
            DisplayMemberPath="ToolTip" // I replaced 'Description' with 'ToolTip'
            ToolTip="{Binding Path=ToolTip}"
            SelectedValuePath="Value" SelectedValue="{Binding Path=Value}"/>

Having replaced Description with ToolTip I can see that the value of ToolTip is appearing on the screen. However

ToolTip="{Binding Path=ToolTip}"

still doesn't work. If I attempt to display ToolTip as follows:

ToolTip="ToolTip" 

it just displays the word 'ToolTip'.

How can I get ToolTip to display a value?

ToolTip="{Binding Path=ToolTip}" binds to ToolTip property of current combo box DataContext (object that contains EnumDataItems property). Assuming you want to set ToolTip of ComboBox to currently selected item's ToolTip property value, this should fix the problem:

ToolTip="{Binding Path=SelectedItem.ToolTip, RelativeSource={RelativeSource Self}}"