Silverlight: Strange Databinding Problem - c#

I am using the Visifire charts to display data on a Windows Phone 7 application.
I created a chart that was properly bound to a dependency property. It worked great. I decided to make the chart into a user control, since I was going to use it in another project as well with the same setup. Now my databinding doesn't work unless I bind it in the code behind rather than in the XAML.
here's what I have:
<UserControl ... x:Name="root">
...
<chart:DataSeries ... DataSource="{Binding ElementName=root, Path=Results}">
...
</UserControl>
and the code behind:
public MyList Results
{
get { return (MyList)GetValue(ResultsProperty); }
set { SetValue(ResultsProperty, value); }
}
// Using a DependencyProperty as the backing store for Results. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ResultsProperty =
DependencyProperty.Register("Results", typeof(MyList), typeof(MyChart), new PropertyMetadata(null));
public GoogleChart()
{
Loaded += delegate
{
// theChart.Series[0].DataSource = Results;
};
Results = new GoogleResults();
InitializeComponent();
}
If I uncomment the line theChart.Series[0].DataSource = Results; it works perfectly. But if I leave that line commented (like I had before I moved the chart to the UserControl) it doesn't bind. (By the way: theChart is the x:name of the parent of the chart. So the first element, .Series[0], references to the chart).
Does anyone know why this would happen? Again, it worked great until I moved the code to a UserControl.
Thanks

If I'm understanding you correctly you've created this UserControl so that you can place instances of it into various pages in your app.
In that case you are likely to be giving those instances a name. That name will replace the name "Root" that is initially assigned in the UserControl's Xaml. Hence the binding for ElementName=Root will fail.
Typically there is a root element (normally a Grid) with the name "LayoutRoot". Hence instead of relying on the UserControl name which can change use "LayoutRoot" which by convention is the Content element for the UserControl. Like so:-
<chart:DataSeries ... DataSource="{Binding ElementName=LayoutRoot, Path=Parent.Results}">
Note the property path now starts with Parent which takes you up to the UserControl without actually needing to know the UserControl's name.

Related

Programmatically setting the content of a ContentControl in WPF

I have a UserControl named "WorkspaceView" and its only purpose is to show other views as tabs. Call these views ViewA, ViewB etc. Which of these views to present should be determined on runtime, so I figured I needed a control that can present ... well ..stuff.
ContentControl to the rescue. Except ... I can't make it work. I'm trying to new up a usercontrol of type ViewA in the code behind and assign it to my MyContent, which is the ContentControl. I've tried:
public WorkspaceView()
{
InitializeComponent();
DataContext = new View(A); //Hoping that the DataContext will propagate down
}
Second attempt was
public WorkspaceView()
{
InitializeComponent();
var binding = new Binding {Source = new ViewA()};
MyContent.SetBinding(ContentControl.ContentProperty, binding);
}
In both cases, I see an empty box, but since I've hard wired a TextBlock into ViewA, I'd expect it to show me that text. What am I doing wrong?
Despite knowing that MVVM is the preferred way to develop WPF applications, I'd prefer to see how I can do this with code behind files. Later on, I will redo the application with MVVM, but first I need to get some basic understanding of WPF.
In response to the suggestions so far, I've tried
MyContent.Content = new ViewA();
but still I the text that is in ViewA does not appear. I've also at the bottom of this post included a screenshot of what the application renders.
WorkspaceView
Resource file
What is rendered
Have you tried simply doing this?
MyContent.Content = new ViewA();
EDIT
Try simplifying your code a bit and working from there. For instance:
public WorkspaceView()
{
InitializeComponent();
// Something better than UserControl should be used here
ObservableCollection<UserControl> views = new ObservableCollection<UserControl>();
views.Add(new ViewA());
views.Add(new ViewB());
DataContext = views;
}
<Border ..>
<TabControl x:Name="TabControl"
..
ItemsSource="{Binding}" />
</Border>
This code sets a WorkspaceView.DataContext to a collection of UserControls. When you specify {Binding} whithin WorkspaceView's XAML you are refering to the whole DataContext object (i.e. your collection.) This way you are setting the TabControl.ItemsSource to your collection of views.
Now you could create DataTemplates targeting the type of each view to control how each control is displayed in its tab within the TabControl.

Application doesn't start if I inherit MainWindow

I have a RootViewModel class, and I want to access an UI element (instantialized in MainWindow) from there. For that I set the class this way:
class RootViewModel : MainWindow, INotifyPropertyChanged
But the application doesn't start. It compiles and throws no error but the Window doesn't appear. If I remove that MainWindow, I can't access my element that has been created in MainWindow.xaml. What can I do to solve this?
EDIT: Ok, I understand that I shouldn't be doing that, it's going against what it is MVVM. But is there a way to modify directly something from MainWindow? What should I try instead of this?
Consider changing RootViewModel to a UserControl. Give it a DependencyProperty called Element, of type UIElement.
Add RootViewModel to the XAML for MainWindow and bind to the element you want to use, like this;
<RootViewModel Element="{Binding ElementName=SourceElement}"/>
WPF windows are objects, so you can always instantiate them manually, like so:
var foo = new FooWindow(); // new Window object
foo.Show(); // show window as non-blocking "dialog"
If you do that, you have access to any public or protected members of the window - that includes any child controls, as long as their Accessibility properties are marked accordingly. So, if FooWindow had a TextBox named txtFooName, you could access it like so:
string name = foo.txtFooName.Text // get string value from textbox
You can also assign to any public/protected members:
foo.txtFooName.Text = "Fizz Buzz, Inc.";
Now, MainWindow is generally set as the StartupUri of the application (in App.xaml), which makes it the entry point for the application, so I'm not entirely sure what you're trying to do.
I was able to achieve what I wanted by creating a
public ObservableCollection<ChartPlotter> myPlotCollection { get; set; }
And then adding a ChartPlotter there, and setting in XAML:
<DockPanel Grid.Column="2">
<ItemsControl Width="Auto"
Height="Auto"
ItemsSource="{Binding myPlotCollection}">
</ItemsControl>
</DockPanel>
So this way I have complete control over what is happening in myPlotCollection[0]. At this moment it's enough for me, later I'll give it another try to bind it properly.

ItemsPanelTemplate in XAML ignores [ContentProperty] attribute

I have a custom Panel where I declared a custom property to hold the content (I don't want to use Children for the content):
[ContentProperty(Name = "PanelContent")]
public class CustomPanel : Panel
{
public static readonly DependencyProperty PanelContentProperty =
DependencyProperty.Register("PanelContent",
typeof(Collection<UIElement>), typeof(CustomPanel),
new PropertyMetadata(new Collection<UIElement>(), null));
public Collection<UIElement> PanelContent
{
get
{
return (Collection<UIElement>)GetValue(PanelContentProperty);
}
}
}
This works perfectly when used like this:
<CustomPanel>
<TextBlock>A</TextBlock>
<TextBlock>B</TextBlock>
</CustomPanel>
But when I want to use the panel as an ItemsPanelTemplate inside ItemsControl, the ContentProperty attribute is ignored and adds everything to the Children collection, not the PanelContent collection:
<ItemsControl ItemTemplate="{StaticResource ReviewTemplate}" ItemsSource="{Binding Reviews}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<CustomPanel></CustomPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
This is not how it should work. According to the documentation:
An ItemsPanelTemplate object element should contain exactly one FrameworkElement-derived class that serves as the root element for items. In most cases this is a Panel-derived class. The expanded template serves as the parent for the realized items and there generally is more than one item. Therefore the XAML content property of the intended root element of an ItemsPanelTemplate should support a collection, as Panel.Children does.
The Panel's GenerateChildren method, that is responsible for this task looks (as seen in ILSpy) like
internal virtual void GenerateChildren()
{
IItemContainerGenerator itemContainerGenerator = this._itemContainerGenerator;
if (itemContainerGenerator != null)
{
using (itemContainerGenerator.StartAt(new GeneratorPosition(-1, 0), GeneratorDirection.Forward))
{
UIElement uIElement;
while ((uIElement = (itemContainerGenerator.GenerateNext() as UIElement)) != null)
{
this._uiElementCollection.AddInternal(uIElement);
itemContainerGenerator.PrepareItemContainer(uIElement);
}
}
}
}
As you can see, it always adds to this._uiElementCollection, which is the field backing the Children property.
I think the ItemsPanelTemplate can only take Panel.Children as the target to layout items. In fact, if you derive your CustomPanel from, say, ContentControl, you will find following exception message in metro style app:
Exception: The ItemsControl.ItemsPanelTemplate must have a derivative of Panel as the root element.
The documentation you linked might be a documentation bug which was copied from WPF time, when you still can insert visuals programmatically into visual tree. In Metro app, there's no longer a way to put your own list of UIElements into visual tree without using Panel.Children.
Because ItemsPanelTemplate is used by ItemsControl to create and use the Panel and it's not XAML that does anything. ItemsControl will simply call Panel.Children.Add no matter what ContentProperty is set to.
Panel is supposed to only layout the children and never style them. So all you must do os only override Arrange and Measure methods. All panels only do that.
For styling and any other logic you must use another approach.

Changing content of Window (WPF)

I've created a simple WPF application which has two Windows. The user fills in some information on the first Window and then clicks Ok which will take them to the second Window. This is working fine but I'm trying to incorporate both Windows into a single Window so just the content changes.
I managed to find this Resource management when changing window content which seems like it is what I'm after. However, I've search for ContentPresenter but couldn't find much help for how I need to use it. For example, if I use a ContentPresenter, where do I put the existing XAML elements that are in the two Windows? I'm guessing the first Window will go into the ContentPresenter but the second one will need to be put somewhere for when it needs to be switched in.
Any help would be great. A simple working example would be even better.
TIA
A ContentPresenter is normally used when restyling existing controls. It is the place where the Content of a control is placed. Instead you should use a ContentControl, which is simply a control that has a content element. Alternatively, you could directly set the Content of your window.
You extract the contents of your two existing windows into two UserControls. Then you create a new Window which will host the contents. Depending on your business logic, you set the content of that window (or that window's ContentControl if you want additional "master" content) to either of those two UserControls.
EDIT:
As a starting point. This is not complete working code, just to get you started. Note that this is bad architecture; you should probably use a MVVM or similar approach once you get this running!
<Window>
<ContentControl Name="ContentHolder" />
</Window>
<UserControl x:Class="MyFirstUserControl" /> <!-- Originally the first window -->
<UserControl x:Class="MySecondUserControl" /> <!-- Originally the second window -->
In code behind of Window:
// Somewhere, ex. in constructor
this.ContentHolder.Content = new MyFirstUserControl;
// Somewhere else, ex. in reaction to user interaction
this.ContentHolder.Content = new MySecondUserControl;
I use ContentPresenter for snapping in content. In the window, I put something like this:
<ContentPresenter Content="{Binding MainContent}" />
In the view model, I have a property called MainContent of type object:
public object MainContent { get { return (object)GetValue(MainContentProperty); } set { SetValue(MainContentProperty, value); } }
public static readonly DependencyProperty MainContentProperty = DependencyProperty.Register("MainContent", typeof(object), typeof(SomeViewModel), new FrameworkPropertyMetadata(null));
Whatever you set MainContent to will show up in the window.
To keep the separation between view and view model, I typically set the MainContent property to another view model and use a data template to map that view model to a view:
<DataTemplate DataType="{x:Type viewmodels:PlanViewModel}">
<views:PlanView />
</DataTemplate>
I put that data template in some central resource dictionary along with a bunch of other view-model-to-view mappers.

XAML inline data binding doesn't work; code behind binding works

Greetings folks!
I'm running into a problem with WPF databinding that I hope you can help out with. I'm new to WPF but an expereienced developer (VB 3.0-6.0, C#).
Here's the scenario:
I have a C# project called MasterPartsData which contains a number of classes which reprsent different types of parts (capacitor, diode, etc). They inherit from a base class called clsPart.
I have another C# WPF project which contains WPF UserControls (as well as a MainWindow) to visually represent the values stored in an individual MasterPartsData (MPD) object. I've created a private field in the usercontrol to hold the object with a getter and setter.
If I create a binding explicitly in the setter for the populated object:
_capacitor = value;
Binding binding = new Binding();
binding.Source = _capacitor;
binding.Path = new PropertyPath("C0uf");
this.txtC0uf.SetBinding(TextBox.TextProperty, binding);
(with _capacitor being the private object variable and C0uf being the property name)
the value correctly displays.
However I don't wish to have to explicitly create each binding in the code behind. My preference is to create the bindings inline in XAML, perhaps with a DataContext pointing to the object.
Unfortunately every different permutation I've tried fails to work; the text box doesn't show data.
I have a couple of suspicions:
1) The binding is correct, but the text box needs to be refreshed.
2) The binding is confused between the private variable and the properties.
3) Maybe the fact that the class is defined in a different project is causing issues.
4) I'm going mad and should check myself into an asylum before someone gets hurt. :)
Any help you can provide would be most appreciated. I'm more than happy to add more information, but didn't want to clutter the question with pages and pages of source.
With respect to your suspicions:
1) I think the default binding behavior of a TextBox is TwoWay, with a LostFocus update trigger, meaning that your UI focus will have to change to another control before the binding will update, if changes are made in the UI.
If changes are made in the code you need to raise the NotifyPropertyChanged event in order for the binding system to see it.
2) This is probably not the case, but it leaves the impression that you're trying to set bindings on your UserControl properties, which is not the way data binding was designed to be used in this particular kind of use case. What you want is to bind data from non-UI classes to dependency properties on your UserControls.
3) This will never matter, as long as your UI project has a reference to your classes.
4) This is a common reaction people have when beginning to use XAML and WPF. It's like instead of being handed a box of Legos, you just got handed an injection molding machine with insufficient instructions, isn't it?
Overall, this is a situation where you might need to examine your design; elements of the "Model-View-ViewModel" pattern will come in handy. If you're unfamiliar with this, it's a development pattern in which you introduce a "ViewModel" class, perhaps you can call it MasterPartsVM which contains an implementation of INotifyPropertyChanged.
The DataContext of your UserControl would be set to this MasterPartsVM class.
A brief code example, using some generic names. Given a ViewModel class with a small backing class that looks like this:
class PartViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public PartClass Data { get; set; }
public String SomeVMProperty
{
get { return Data.SomeProperty; }
set
{
if (Data.SomeProperty != value)
Data.SomeProperty = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("SomeVMProperty"));
}
}
}
class PartClass
{
public string SomeProperty { get; set; }
}
The XAML of a basic UserControl would look like this:
<UserControl x:Class="WpfApplication1.PartUserControl"
... >
<Grid>
<TextBox Text="{Binding SomeVMProperty}" Margin="68,77,104,176" />
</Grid>
</UserControl>
To connect your data class to this UserControl, you set the UserControl's DataContext property. If you do this in code, it's a matter of having a reference to your user control and the ViewModel, and then setting the property:
MyUserControlInstance.DataContext = new PartViewModel(); // or some existing PartViewModel
That combination of code should work to produce a textbox whose Text property changes every time the SomeVMProperty property is changed.
In a basic binding scenario, if your class looks like this
public class MasterPartsData
{
private string _c0uf;
public string C0uf
{
get { return _c0uf;}
set { _c0uf = value;}
}
public MasterPartsData()
{
C0uf = "Hello World!";
}
}
your XAML would look like this
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" >
<Window.DataContext>
<local:MasterPartsData />
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding Path=C0uf}" />
</Grid>
</Window>
Note, there are many different approaches to setting the DataContext, you don't necessarily just have to do it in the XAML
Also, typically your MasterDataParts class would implement INotifyPropertyChanged

Categories