How to bind XmlDataProvider.Source to MVVM property - wpf

I've got a treeview bound to an XmlDataProvider following this example. The app I am working on is following the MVVM pattern and the Xml is from a file that the user will open.
When I try to bind the Source property of the XmlDataProvider like so
<XmlDataProvider Source="{Binding Path=XmlFilePath}"/>
I get a "Binding can only be applied to a DependencyProperty of a Dependency object." or somesuch.
So short of cobbling the binding together procedurally is there a way to declaratively bind the XmlDataProvider Source? If I try to forgo the data provider and bind the tree directly to an XmlNode property I get an error about using XPath binding only with Xml objects; which makes absolutely no sense to me but I'm sure it's trying to tell me something important.

The answer appears to be: you can't.
I was able to solve my underlying problem (binding a treeview to an Xml document) by removing the XmlDataProvider from the equation and binding the TreeView directly to a ViewModel property that returns an XmlNode.
What had been tripping me up was that I took the binding code that pointed at the XmlDataProvider and pointed it at my property, leaving the XPath argument in place.
<TreeView ItemsSource="{Binding Path=ProjectDocument XPath=.}">
This would result in a runtime error: System.Windows.Data Error: 44 : BindingExpression with XPath cannot bind to non-XML object.; XPath='.'
Which was not the most helpful. What it was really trying to say is that you can't bind to an XmlNode property AND provide an XPath argument in the binding (because it's the XmlDataProvider that knows what to do with that??).
<TreeView ItemsSource="{Binding Path=ProjectDocument}">

actually that was rather tough problem for me, cause I needed the app to load treeview from temp file, and assuming application can have different locations, I can't set strict link in the XmlDataProvider Source property;
Add source as resource to the project
the solution I found is adding temp file (markup is created via XAML, see below) to the project with build action set to Content thus, application reloads it every time you call InitializeComponent() on the object containing XmlDataProvider and my treeview updates.
<XmlDataProvider x:Key="dshPreview"
Source="~tmpConstruct.xml"
XmlNamespaceManager="{StaticResource argNms}"
IsAsynchronous="true"/>
TreeView is bound like this:
<TreeView x:Name="PreviewTree"
ItemsSource="{Binding Source={StaticResource dshPreview},
XPath=/mns:engine/mns:ws}"
/>
Maybe this will help someone

I didn't find how to bind the source straight away, but you can change the XmlDataProvider source in the code behind as following:
var xdp = (XmlDataProvider)this.Resources["key-of-your-XmlDataProvider-in-resources"];
xdp.Source = new Uri("http://url-of-your-xml");
You can use that combined with an event handler to bind.

Related

How can I bind Text property of TextBox (VIEW) to a vaiable (in VIEWMODEL)

I am a newbie in WPF. I was exploring MVVM Pattern for WPF applications. I am having trouble in binding Text property of a TextBox from VIEW to a variable in VIEWMODEL
Here is the TextBox from MainWindow.xaml
<TextBox x:Name="UsernameTxt" Grid.Row="4" materialDesign:HintAssist.Hint="Username"/>
I just need to know how to bind its Text Property to ViewModel Class in Class Library
Thanks
I think it's possible to give a very generic answer to this very generic question.
If the question changes context this answer is very likely to be deleted but here goes anyhow.
You want your viewmodel to be in the datacontext of the textbox. Because datacontext is inherited down the visual tree this usually means you want to set datacontext of your window to an instance of the viewmodel. Or maybe the usercontrol your textbox is in, but we know nothing about your app so let's just cover the simple scenario.
Your options are to instantiate a viewmodel using code or xaml.
If you look at this article:
https://social.technet.microsoft.com/wiki/contents/articles/31915.wpf-mvvm-step-by-step-1.aspx
That instantiates in xaml.
Note the xmlns is
xmlns:local="clr-namespace:wpf_MVVM_Step01"
That's saying where you see some bit of markup which is prefaced "local:" then go get the class out of this namespace.
To point to a different dll ( a class library ) you need to tell it which assembly. You do that by adding ;assembly=Whicheverdll to your equivalent of that xmlns. And of course that won't be local then so give it a different name. You also need a reference to that dll or project added to the entry point exe.
Once you've done all that and your viewmodel is instantiated into memory and in the datacontext of that textbox you need some sort of binding.
Which the article covers but that will be something like:
<TextBox Text="{Binding YourPublicStringProperty}"/>

WPF: Does static Resource with Binding as "TwoWay' supports two way binding?

<TextBox Name="CustomerName" Height="30" Margin="5"
Text="{Binding Source={StaticResource MyCustomerData},
Path=CustomerName, Mode=TwoWay}" Grid.Row="0" Grid.Column="1" />
Above is my xaml snippet
MuCustomerData is my class which implements INotifyPropertyChanged interface and it has a property CustomerName which shall notify about the changes using PropertyChanged event to the View (UI)
Here I bind my class(MyCustomerData) as StaticResource and binding mode as TwoWay. Will this actually work two way? or I should use my class as Dynamic Resource to make the binding work two way?
Since only once a StaticResource would get loaded and any further changes will not be taken when it is staticresource ....
your thoughts?? I just read this http://www.codeproject.com/Articles/393086/WPF-StaticResource-vs-DynamicResource ..Pls do take a look on this article ..
StaticResource and DynamicResource refer to WPF's Resources, not Bindings.
StaticResource evaluates once (usually when the XAML is parsed), and is never evaluated again because WPF assumes the resource is static and won't ever change.
DynamicResource means the resource is dynamic, so evaluate it whenever the value is needed.
In your case, the Source property of your binding will be evaluated once, and never again since it is set to a StaticResource. If you change the MyCustomerData object to a new object, the binding won't evaluate itself again to reflect the change.
But the actual property, CustomerName, will get updated as needed because it is bound using a TwoWay binding.
To look at it another way, you're creating a binding that says something like
var b = new Binding();
b.Source = MyCustomerData;
b.Path = "Name";
b.Mode = TwoWay;
When you set b.Source using a StaticResource, then think of the binding evaluating using b.Source.Name. But if you were to use a DynamicResource, think of it as evaluating with MyCustomerData.Name, and so it would use the current version of MyCustomerData.
So you could set MyCustomerData = new MyCustomerData();` after the binding has been evaluated once, and a dynamic resource would evaluate that correctly, while a static resource would not.
StaticResource or DynamicResource have nothing to do with Binding.
I will try to explain this as simple as possible for you.
StaticResource means that you know in which resource dictionary the object with specific key is being hold. Thats it. Nothing else. You know its either in StackPanel.Resources or maybe Window.Resources.
DynamicResource is gonna be used when you add/load somewhere in your VisualTree an object with a key into resources dictionary. It's position is unknown at app's startup and so it needs to be loaded dynamically.
Remember no matter what kind of language you use for programming whenever you see dynamics being mentioned it means something is happening at runtime and not right from beginning.
Best example would be dynamically generated objects in C#. Those objects allow you to define a property at runtime. When the app is started the object is pretty empty.
Now back to Binding. In your case you tell the Binding where the Source is and from that point on Binding will work without giving a s**t how is Source defined.

DataBinding to global ObservableCollection

I have a WPF application using MVVM pattern, where I have many ObservableCollections. Instead of putting these ObservableCollections into each ViewModel, I placed them into a static class called Observables, which is a member of static class AppCommon. So I can access all observable collections through AppCommon.Observables.AnyObservableINeed.
Now I need to change bindings of UserControl's so they bind to these global ObservableCollection's but I don't know how to refer to these ObservableCollections without changind the DataContext.
I tried adding namespace like
xmlns:globals="clr-namespace:Demirbaş.Globals"
and then in the ListBox setting the ItemsSource property like
<ListBox ItemsSource="{Binding Source={globals:Observables.TaşınırSınıfları}}"
but that would give me following error:
'{globals:Observables.TaşınırSınıfları}' value is not a valid MarkupExtension expression. Cannot resolve 'Observables.TaşınırSınıfları' in namespace 'clr-namespace:Demirbaş.Globals'. 'Observables.TaşınırSınıfları' must be a subclass of MarkupExtension.
What is the problem here? Am I using the right XAML syntax to bind to these collections?
EDIT
ItemsSource="{Binding Source={x:Static globals:AppCommon.Observables.TaşınırSınıfları}}" gives me error :
Cannot find the type 'AppCommon.Observables'. Note that type names are case sensitive.
I think it cannot refer to nested classes, is it right? What's the solution?
Thanks
I don't know the namespace of your application but try
xmlns:local="clr-namespace:Demirbaş"
<ListBox ItemsSource="{Binding
Source={x:Static local:AppCommon+Observables.TaşınırSınıfları}}" />
You need to use the x:Static markup extension like LPL suggested in a comment to tell WPF it's a static object
<ListBox ItemsSource="{Binding
Source={x:Static globals:Observables.TaşınırSınıfları}}" />
This error can also occur when the namespace reference is not fully qualified and the target binding exists in another assembly.
For example, xmlns:l="clr-namespace:AssemblyA.Namespace;assembly=AssemblyA".
If the specific assembly is not specified, the same error message will be displayed "value is not a valid MarkupExtension expression".

loading a silverlight control with a custom constructor

I have a silverlight page in which I am loading a control. This control has its own viewmodel which I pass in to the .xaml.cs file thru its constructor. However I get an error when compiling. This is the error:
{No matching constructor found on type 'MySite.Views.SearchFlyOutWin'}
My main page makes a reference to the 'SearchFlyOutWin' like this
xmlns:part="clr-namespace:MySite.Views;assembly=MySite"
In my mainpage.xaml I have tried to load the control like this
<part:SearchFlyOutWin x:Name="searchFlyOutWin" Visibility="{Binding Converter={StaticResource BooleanToVisibilityConverter}, Path=IsSearchVisible}" />
The constructor in my SearchFlyOutWin.xaml.cs is like this
public SearchFlyOutWin(ISearchFlyoutViewModel viewmodel)
{
InitializeComponent();
DataContext = viewmodel;
}
I get the error described above in my Mainpage.xaml.cs when it calls the InitializeComponent(); method.
I think I probably need to direct the clr to call the correct constructor when loading the searchwin in this line here below
<part:SearchFlyOutWin x:Name="searchFlyOutWin" Visibility="{Binding Converter={StaticResource BooleanToVisibilityConverter}, Path=IsSearchVisible}" />
Any ideas on how to correct this? ...Thanks for your time.
.
I have a silverlight page in which I
am loading a control. This control has
its own viewmodel which I pass in to
the .xaml.cs file thru its
constructor. However I get an error
when compiling. This is the error:
{No matching constructor found on type
'MySite.Views.SearchFlyOutWin'}
If your own control's constructor takes some parameter(s), then you cannot use this control in XAML. In XAML, every control must have a constructor with no parameter. That is why, it shows the error message {No matching constructor found on type 'MySite.Views.SearchFlyOutWin'}, since XAML parser searches a constructor with no parameter in your control called SearchFlyOutWin, and it found none!
One soution is that remove the parameter from constructor, and define your Model in the XAML as resource, then set the DataContext to it. Like this,
<Window.Resources>
<local:SearchFlyoutViewModel x:Key="model"/>
</Window.Resources>
<part:SearchFlyOutWin DataContext="{StaticResource model}"/>
Hope, it solves your problem.
.
If you're committed to passing the viewmodel to the object in the constructor (which I don't think is a bad thing), the only way I've found to do this is to create the object in code and then add it to its parent panel programatically. Setting up bindings in code is also possible, though the syntax is more complex than the XAML syntax. The code might looks something like:
SearchFlyOutWin searchFlyOutWin = new SearchFlyOutWin(viewModel);
Binding b = new Binding("");
b.Source = IsSearchVisible;
b.Converter = new BooleanToVisibilityConverter();
searchFlyOutWin.SetBinding(SearchFlyOutWin.VisibilityProperty, b);
SearchFlyOutWinParentPanel.Children.Add(searchFlyOutWin);
Where SearchFlyOutWinParentPanel is some panel that can accept children. If there's an alternate way to do this in XAML, I'd love to see it, but I haven't found it yet.
You may need to set you viewmodel class to be public.
Because I guess your viewmodel class will be in another namespace other than view.

Cloning controls and databinding

I use the Justin-Josef Angel "way" of cloning controls, but I've run into a problem with data binding: If for example I set the Text property to be bound to something like "{Binding Name}" and then clone the control, the new cloned control will have the text property set to for example Joe (the evaluated value) and not the binding expression.
Anybody have an idea about how to clone a control and have the cloned control be databound?
Sounds like you're serializing an already-databound control back to xaml in order to deserialize a clone of it. The problem being that the xaml writer doesn't serialize bindings, it evaluates them and serializes the result.
There are two ways around this. First, keep a copy of the original xaml around, with the Binding markup extensions present, and deserialize that. Second, create a TypeConverter that the xaml writer will use to serialize bindings. You can find a couple articles about doing this here and here.

Resources