WPF designer issue referencing enum - c#

So I have referenced an enum in this example using the staticextension, which works fine at runtime, but fails at design time due to the error "Could not create an instance of type 'StaticExtension'."
I understand this to mean that it thinks it needs an instance of the enum type to actually reference it. However, the enum is defined in the window as static so I don't understand why it is having issues.
Is there any reasonable way to keep the designer working? The closest I have found so far is to put it in an objectdataprovider and create methods to return the enum values. Behind the scenes this is basically creating an object to reference the static type and seems like too much work to just pull the enum values out. The goal here is just to be able to reference individual enum types and display them.
<Window
x:Class="DaedalusGraphViewer.GraphViewerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DaedalusGraphViewer="clr-namespace:DaedalusGraphViewer">
<StackPanel>
<Label Content="testtesttest">
<Label.ContextMenu>
<ContextMenu>
<MenuItem Header="{x:Static DaedalusGraphViewer:GraphViewerWindow+Test.test1}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
</StackPanel>
</Window>
C#:
using System.Windows;
namespace DaedalusGraphViewer
{
/// <summary>
/// Interaction logic for GraphViewerWindow.xaml
/// </summary>
public partial class GraphViewerWindow : Window
{
public GraphViewerWindow()
{
InitializeComponent();
Application.Current.MainWindow = this;
}
public enum Test
{
test1, test2
}
}
}

This is a known bug in designer. The workaround is not to use nested types with designer.

Related

WPF Command binding to a static viewmodel

Firstly, I am new to WPF and MVVM, am trying hard to write well structured/separated code so please be kind.
I have created a user control and its own separate view model. In the view model I have an ICommand which relays to a method in the same viewmodel. I bind to this command in the XAML using System.Windows.Interactivity on an event like so:-
<UserControl x:Class="MyNamespace.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:MyNamespace"
mc:Ignorable="d"
Height="300"
d:DesignHeight="300" d:DesignWidth="1500"
IsManipulationEnabled="True"
Background="{StaticResource BackgroundWhiteBrush}">
<Grid
d:DataContext="{x:Static local:MyControlDesignModel.Instance}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseUp">
<i:InvokeCommandAction Command="{Binding MyViewModelCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
My code behind (which I'm trying to leave as empty as possible) looks like this:
namespace MyNamespace
{
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : UserControl
{
public MyControl()
{
DataContext = ViewModelMyControl;
InitializeComponent();
}
}
}
I want to be able to use this control in several pages. I also want to be able to call a method in this view model (passing a parameter) from other view models to allow it to update itself from a datastore.
I used a DI container to provide a reference to the view model so that I can a) reference its data loading method from another place and b) set that to the DataContext in the code-behind (above).
The implementation of the DI container provides this as follows:
/// <summary>
/// A shortcut to access the <see cref="MyControlViewModel"/>
/// </summary>
public static MyControlViewModel ViewModelMyControl => Framework.Service<MyControlViewModel>();
With this DI referenced viewmodel on the DataContext, the event/command does not fire.
If I change the code behind to as follows, the event/command does fire but then I lose the static reference which I was trying to have to "hold" the data between pages. I seem to be able to have events or static reference but not both.
namespace MyNamespace
{
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : UserControl
{
public MyControl()
{
DataContext = new MyControlViewModel();
InitializeComponent();
}
}
}
I think it has something to do with the ViewModel lifecycle or perhaps binding in general. I have been following a lot of guides and now find myself stuck.
How do I have event/commands firing and also maintain a reference to the user control's data between pages which use it?

Set language of the DocumentViewer to German (from code, not XAML)

I am trying to change the language of the DocumentViewer from default English to German but with no success.
Being new to WPF, I really struggle to do this.
IMPORTANT: DocumentViewer is created in code behind, in response to the menu item click, and then it is added as main window's Content.
I have tried doing the following, but it seems to do nothing:
myDocumentViewer.Language = System.Windows.Markup.XmlLanguage.GetLanguage("de-DE");
No changes are made, DocumentViewer keeps English.
Googling for proper usage of the Language property, I found nothing useful.
QUESTION:
How can I set the language of the DocumentViewer (created with code) to German?
What you are trying to accomplish can be done, but not very easily.
I'll start by pointing out that your test machine needs to have the appropriate language resources installed to permit DocumentViewer to show you tooltips etc. in German. In practice, this means that you'll need to have German (Germany) language pack installed on your computer. See Language Packs for details.
What comes below is what I know to the best of my understanding:
WPF does not quite have an in-built infrastructure, as far as I can tell, to dynamically adapt to changes in either Thread.CurrentThread.CurrentUILanguage or to changes in xml:lang(which is equivalent to FrameworkElement.Language property.
WPF controls primarily utilize xml:lang to determine their UI language (assuming that the corresponding UI resources are available), and it is up to the application developer to hook that up with Thread.CurrentThread.CurrentUILanguage if so desired. This in itself is not very hard to do using data-binding, like this:
<DocumentViewer Language="{Binding UILanguage, ConverterCulture={x:Static glob:CultureInfo.InvariantCulture}}" />
That still does not mean that the control thus data-bound would adapt its UI language to changes in Thread.CurrentThread.CurrentUILanguage. Every time you need the UI language to be changed, you need to recreate the control, remove the old control from the visual tree, and add the new one. Roughly, the code would look somewhat like this:
private void ChangeCulture()
{
string ietfLanguageTag = "de-DE";
var cultureInfo = CultureInfo.GetCultureInfo(ietfLanguageTag);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
UILanguage = ietfLanguageTag;
var parent = VisualTreeHelper.GetParent(_documentViewer) as Grid;
int index = parent.Children.IndexOf(_documentViewer);
parent.Children.Remove(_documentViewer);
_documentViewer = new DocumentViewer();
parent.Children.Add(_documentViewer);
}
The above snippet assumes that the visual parent of the DocumentViewer is a Grid, and it is backed by the variable _documentViewer.
Generally, the above solution is too simplistic and is not well suited for MVVM scenarios (which is often the case in WPF applications). You might have data bindings to the DocumentViewer instance, and creating new instances would require that those bindings be recreated in code (if, on the other hand, there happen to be no data-bindings involved, and all settings are set in code, then the above approach would just work, I think).
You can further improve this by creating a simple user control that encapsulates a DocumentViewer, along with any interesting bindings you might wish to preserve. Your control would look like this:
XAML:
<UserControl x:Class="LocalizedDocumentViewer.CultureAwareDocumentViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:glob="clr-namespace:System.Globalization;assembly=mscorlib"
xmlns:local="clr-namespace:LocalizedDocumentViewer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<DocumentViewer DataContext="{Binding}" Language="{Binding UILanguage, ConverterCulture={x:Static glob:CultureInfo.InvariantCulture}}" />
</Grid>
XAML.cs
using System.Windows.Controls;
namespace LocalizedDocumentViewer
{
public partial class CultureAwareDocumentViewer : UserControl
{
public CultureAwareDocumentViewer()
{
InitializeComponent();
}
}
}
Now, you can easily include this in your main application UI, like shown below. The XAML below includes a couple of additional UI elements (buttons and labels) that would help show a complete example:
MainWindow XAML:
<Window x:Class="DocViewerLoc.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DocViewerLoc"
xmlns:localizedDocumentViewer="clr-namespace:LocalizedDocumentViewer;assembly=LocalizedDocumentViewer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="DocumentViewer Culture Change Demo"
Width="525"
Height="350"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid>
<!-- Row and Column Definitions -->
<!-- Define a small row on the top of the window to place buttons -->
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<!-- Controls -->
<Button Grid.Row="0"
Grid.Column="0"
Command="{Binding CultureChanger}"
CommandParameter="{Binding RelativeSource={RelativeSource Self},
Path=Content}">
en-us
</Button>
<Button Grid.Row="0"
Grid.Column="1"
Command="{Binding CultureChanger}"
CommandParameter="{Binding RelativeSource={RelativeSource Self},
Path=Content}">
de-DE
</Button>
<Label Grid.Row="0" Grid.Column="2"><-- Click on one of these buttons to change UI culture</Label>
<Grid Grid.Row="1" Grid.ColumnSpan="3">
<localizedDocumentViewer:CultureAwareDocumentViewer x:Name="_documentViewer" DataContext="{Binding}" />
</Grid>
</Grid>
The corresponding code-behind has a couple of dependency properties used to help communicate with the bindings in the above XAML.
MainWindow.xaml.cs
using System;
using System.Globalization;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace DocViewerLoc
{
public partial class MainWindow : Window
{
public MainWindow()
{
CultureChanger = new SimpleCommand(ChangeCulture);
InitializeComponent();
}
/// <summary>
/// ChangeCulture is called when one of the buttons with caption
/// 'en-us' or 'de-DE' is pressed.
/// </summary>
/// <param name="parameter">
/// A string containing the caption 'en-us' or 'de-DE'.
/// </param>
private void ChangeCulture(object parameter)
{
string ietfLanguageTag = parameter as string;
if (ietfLanguageTag == null) return;
var cultureInfo = CultureInfo.GetCultureInfo(ietfLanguageTag);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
// This will ensure that CultureAwareDocumentViewer's Language property
// binds to the updated value set here when it is instantiated next.
UILanguage = ietfLanguageTag;
// Remove the old instance of _documentViewer from the UI.
var parent = VisualTreeHelper.GetParent(_documentViewer) as Grid;
int index = parent.Children.IndexOf(_documentViewer);
parent.Children.Remove(_documentViewer);
// Create a new instance of CultureAwareDocumentViewer. This will
// use the updated value of UILanguage bind it to its Language (xml:lang)
// property, thus resulting in the appropriate language resources being
// loaded.
_documentViewer = new LocalizedDocumentViewer.CultureAwareDocumentViewer();
// Now, add the _documentViewer instance back to the UI tree.
parent.Children.Add(_documentViewer);
}
/// <summary>
/// ICommand used to bind to en-us and de-DE buttons in the UI
/// </summary>
#region CultureChange
public SimpleCommand CultureChanger
{
get { return (SimpleCommand)GetValue(CultureChangerProperty); }
set { SetValue(CultureChangerProperty, value); }
}
// Using a DependencyProperty as the backing store for CultureChanger. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CultureChangerProperty =
DependencyProperty.Register("CultureChanger", typeof(SimpleCommand), typeof(MainWindow), new PropertyMetadata(default(SimpleCommand)));
#endregion
/// <summary>
/// UILanguage property used to bind to the FrameworkElement.Language (xml:lang) property
/// in the DocumentViewer object within the CultureAwareDocumentViewer control.
/// </summary>
#region UILanguage
public string UILanguage
{
get { return (string)GetValue(UILanguageProperty); }
set { SetValue(UILanguageProperty, value); }
}
// Using a DependencyProperty as the backing store for UILanguage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UILanguageProperty =
DependencyProperty.Register("UILanguage", typeof(string), typeof(MainWindow), new PropertyMetadata(Thread.CurrentThread.CurrentUICulture.IetfLanguageTag));
#endregion
}
/// <summary>
/// Simple implementation of the ICommand interface that delegates
/// Execute() to an Action<object>.
/// </summary>
public class SimpleCommand : ICommand
{
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore 67
public SimpleCommand(Action<object> handler)
{
_handler = handler;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_handler?.Invoke(parameter);
}
private Action<object> _handler;
}
}
The below screenshot show the resulting application UI. Note that the resources in DocumentViewer would switch between English and German, but the rest of the UI would not (because we did not try to localize our application!).
Application showing en-us resources in DocumentViewer:
Application showing de-DE resources in DocumentViewer:
AFAIK you are setting it correctly.
I do not have experience with DocumentViewer, but setting CurrentUICulture does not translate. Setting CurrentUICulture selects between resources that you have in your application for different languages. See https://stackoverflow.com/a/1142840/5569663 for an example. I assume that Language of a DocumentViewer is the same.
Unluckily setting the Language property of your DocumentViewer won't work. The reason of this issue is related to the PresentationUI assembly (it is a standard one), which contains resources which affect DocumentViewer.
The main point is that tooltips and labels are hardcoded in these resources and they are only in english (at least considering .NET 4).
For example, this is how the DocumentViewer's print button is defined (in the themes/generic.baml resource):
<Button Name="PrintButton" x:Uid="Button_14" ToolTip="Print (Ctrl+P)" ToolTipService.ShowOnDisabled="True" Width="24px" Padding="2,2,2,2" Margin="2,2,2,2" VerticalAlignment="Center" Command="ApplicationCommands.Print" IsTabStop="True" TabIndex="0" Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type ui:PresentationUIStyleResources}, ResourceId=PUIDocumentViewerButtonStyle}}" Background="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type ui:PresentationUIStyleResources}, ResourceId=PUIDocumentViewerPrintButton}}" CommandTarget="{Binding Path=TemplatedParent, RelativeSource={RelativeSource TemplatedParent}}" />
As you can see is defined as "Print (Ctrl+P)". You will find the same situation for other labels which you would localize. Even if x:Uid properties are defined LocBaml won't work since PresentationUI is not a satellite assembly.
So a first solution could be: write your own DocumentViewer style and you can use the language that you prefer. The problem is that inside a DocumentViewer there is a control named FindToolBar. It is declared as internal, so probably it would be hard to redefine its style.
Then I propose an alternative solution to you. My idea is based on the fact that localizable children of a DocumentViewer have a name (you can use ILSpy to establish it).
So you need just to extend DocumentViewer in this way:
public class LocalizedDocumentViewer : DocumentViewer
{
public LocalizedDocumentViewer()
{
Loaded += new RoutedEventHandler(OnLoaded);
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Button button = FindChild<Button>(this, "PrintButton");
button.ToolTip = Properties.Resources.PrintToolTip;
button = FindChild<Button>(this, "CopyButton");
button.ToolTip = Properties.Resources.CopyToolTip;
button = FindChild<Button>(this, "FindPreviousButton");
button.ToolTip = Properties.Resources.FindPreviousToolTip;
button = FindChild<Button>(this, "FindNextButton");
button.ToolTip = Properties.Resources.FindNextToolTip;
Label label = FindChild<Label>(this, "FindTextLabel");
label.Content = Properties.Resources.FindTextLabel;
/* and so on... */
}
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
/* see the link for the code */
}
}
You can find the code of FindChild method here (take a look to CrimsonX's answer).
I know, it is a unelegant solution. I do not like it too. But I guess it is fast and it allows you to preserve the default style look.

Restricting Input in WPF

I am creating a custom control that when invoked in the XAML can be set to only allow certain types of inputs:
<lib:CustomControl RestrictTo="UnsignedIntegersOnly" ... ></CustomControl>
Where the UnsignedIntegersOnly is part of an Enum containing the set of allowed restrictions.
If the user inputs something that is not allowed, the control will throw a validation error and not allow him to continue to the next form/page/etc.
My vision for implementing this, was to, in the underlying TextBox that makes up this control, bind its text field to a validation rule which will be passed as an input the RestrictTo value that was specified in the CustomControl XAML declaration. Then in that ValidationRule class, handle the RestrictTo specific validation and return whether the validation was successful or not.
This is where I am not quite sure how to proceed. Is it even possible to pass arguments to the ValidationRule in such a seemingly dynamic manner? I am setting a property, RestrictTo, of my control and then passing that to its validation.
If it is possible, how would it be done? What sort of binding or resource linking should I use?
You might be interested in using a MaskedTextBox control, it will restrict what the user can input in the TextBox.
As there's no official control from Microsoft for WPF I would suggest the following from Xceed :
MaskedTextBox (it's free to use :-)
Here you have the syntax of the mask :
http://msdn.microsoft.com/en-us/library/system.windows.forms.maskedtextbox.mask.aspx
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="350" Width="525">
<Grid>
<xctk:MaskedTextBox Mask="0000"></xctk:MaskedTextBox>
</Grid>
</Window>
If you have Visual Studio 2012 you can easily get this package through NuGet :
(right-click on your project )
Note : on my answer to your previous question, I extensively used validation rules in the link I posted but I'd say that if there are some times where you can avoid it through the means of a well-crafted component/control, then it's wise to do so. As #Barn pointed out on your previous question, a generic validation rule might be a hard thing to do and somewhat questionable as you'll have to handle all the types in it, IMO it's a little counter-intuitive as validators and converters are generally specific against being generalist; you're likely to waste more time on it than it's worth and it will be probably less re-usable than you think it could be. (source : my experience)
Below code should get you started. It is a user control and not a custom control. I recomend you get your code working as a user control first and then convert it to a custom control. Bind Valid property to some property in your viewmodel that controls user workflow.
XAML:
<UserControl x:Class="WpfApplication.ValidatingControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" >
<StackPanel Orientation="Horizontal">
<TextBox Name="_textBox" TextChanged="OnTextChanged" Background="LightGray" Width="200"/>
<TextBlock Name="_messageText" Foreground="Red" />
</StackPanel>
</UserControl>
Code behind:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication
{
public partial class ValidatingControl : UserControl
{
public ValidatingControl()
{
InitializeComponent();
}
public enum Restrictions
{
UnsignedIntegersOnly,
SmallIntegersOnly
}
public static readonly DependencyProperty RestrictToProperty =
DependencyProperty.Register("RestrictTo", typeof(Restrictions), typeof(ValidatingControl), new PropertyMetadata(Restrictions.UnsignedIntegersOnly));
public Restrictions RestrictTo
{
get { return (Restrictions)GetValue(RestrictToProperty); }
set { SetValue(RestrictToProperty, value); }
}
public bool Valid
{
get { return (bool)GetValue(ValidProperty); }
set { SetValue(ValidProperty, value); }
}
public static readonly DependencyProperty ValidProperty =
DependencyProperty.Register("Valid", typeof(bool), typeof(ValidatingControl), new UIPropertyMetadata(false));
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
ValidateText(RestrictTo, _textBox.Text);
}
private void ValidateText(Restrictions restrictTo, string text)
{
// validate text, update _messageText, update Valid
}
}
}

WPF Combobox deriving from derived doesn't work

HOPEFULLY, someone strong in WPF knows what's going on... The scenario I've sampled below is also applicable to others too, like textbox, command buttons, etc...
I'm playing with creating custom user controls... Ex: working with a simple Combobox. In one project class library LibA I've created some samples derived from... ex: TextBox, ComboBox, Window, etc. A second library LibB I'm creating another class derived from ex: Combobox in LibA... Otherwise, no problem.... done similar thing in C# WinForms with no problems.
Now, the problem, I drag the control (from LibB) onto the first "Window" (native so no derivation issues) of the app, save and run. The derived library doesn't even hit its constructor which I just put a simple command just test it was getting created properly worked or not, but its not... In the XAML of the form, it is properly referencing both namespace projects, so I know that appears correct.
So, I then created a derived combobox in the same original LibA, put that on the form, and IT properly went into the constructor.
Here's a snippet of what I have going on.
namespace LibA
{
public class MyCombo1 : ComboBox
{
public MyCombo1()
{ ToolTip = "this is my base declaration"; }
}
public class MyCombo1b : MyCombo1
{
public MyCombo1b() : base()
{ ToolTip = "this constructor IS reached"; }
}
}
In a separate project (library),
using FirstLibraryThatHas_MyCombo1
namespace LibB
{
public class OtherLibCombobox : MyCombo1
{
public OtherLibCombobox() : base()
{ ToolTip = "this version is NOT being recognized in the window"; }
}
}
So, neither of these are visually designed, they are all in code only... In addition, I've done it with the TextBox control too, same results... It doesn't stop in the debugger... Any ideas?
Although I've changed actual names from sample, here's a brand new window, one with original class declaration AND one with the DERIVED version.. Here's a full XAML test window
<Window x:Class="MyProject.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="300" Width="300"
xmlns:my="clr-namespace:WPFGUI;assembly=WPFGUI"
xmlns:my1="clr-namespace:DerivedControls;assembly=DerivedControls">
<Grid>
<my:MyComboBoxClass
Name="MyComboBoxInWindow"
HorizontalAlignment="Left"
Height="23"
Width="120" />
<my1:cboDerivedComboClass
Name="cboDerivedComboInWindow"
Height="23"
HorizontalAlignment="Left"
Width="120" />
</Grid>
</Window>
Isn't this making a circular reference?
You call MyDerivedControl that is in another assembly, and DerivedControl needs the primary assembly
because it inherits a type you defined there.
And then, you try to display it in a window from the primary assembly?
Try to clean and rebuild your project.
I've tried and failed to reproduce the problem. I think you have a different problem, though. If you use the xaml above - the number two combobox will completely cover the first - thus you will not be able to get the tooltip...
Also, check that all assemblies target the same framework version.

Binding ListBox ItemsSource to method call results at compile time?

I'm new to WPF data binding.
I have a ListBox on a form that I want to bind to the results of the following method call:
RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)
.OpenSubKey(#"SOFTWARE\Vendor\Product\Systems").GetSubKeyNames();
At the moment I'm doing it at runtime by assigning ListBox.ItemsSource = (method); in the Window_Loaded() event handler. But this means that the source data for the control is non-obvious when looking at the control configuration in the form editor.
Is there a way to configure this binding within the XAML so that it is visible in the form editor, to make the behavior of the code easier to understand?
Most of the examples in the MSDN documentation bind the controls to static resources, like in-line XAML resources. I've noticed that there is an ObjectDataProvider class which provides "[...] the ability to bind to the result of a method." However I am finding the examples in the ObjectDataProvider documentation quite confusing. I'd appreciate some advice on whether that's the right way to do this binding, and if so, what syntax to use when declaring the ObjectDataProvider.
In short, I don't think you can use such a complex statement directly in your XAML. As you've found, it is possible to bind to the result of calling a method of an object via ObjectDataProvider, but your expression is a chain of method calls that I believe cannot be used to source ObjectDataProvider directly in XAML.
You should instead think about implementing a separated presentation pattern such as Model-View-ViewModel to expose the result of your expression via a collection property on a ViewModel that you then bind as the DataContext of your view (Window).
Something like:
MainWindow.xaml
<Window x:Class="WpfApplication10.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>
<ItemsControl ItemsSource="{Binding Items}"/>
</Grid>
</Window>
MainWindow.cs
using System;
using System.Collections.Generic;
using System.Windows;
using Microsoft.Win32;
namespace WpfApplication10 {
public class ViewModel {
public IEnumerable<String> Items {
get { return RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(#"SOFTWARE\Vendor\Product\Systems").GetSubKeyNames(); }
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
DataContext = new ViewModel();
}
}
}

Categories