I am trying to inject a LayoutGrid and a canvas into my windows, but this causes a little headache:
Here is my WindowBase class:
public class WindowBase : Window
{
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
if (Content is FrameworkElement)
{
var originalContent = Content as FrameworkElement;
var grid = new Grid();
grid.DataContext = originalContent.DataContext;
Content = grid;
grid.Children.Add(originalContent);
var canvas = new Canvas() { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch };
grid.Children.Add(canvas);
}
}
}
My MainWindow thats inheriting from WindowBase looks like this:
XAML:
<local:WindowBase x:Class="InsertCanvasTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:InsertCanvasTest"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Border>
<Grid>
<ComboBox SelectedIndex="1"
ItemsSource="{Binding ItemSource1}" />
</Grid>
</Border>
</local:WindowBase>
Code Behind of MainWindow:
public partial class MainWindow : WindowBase
{
private List<int> _itemSource1;
public List<int> ItemSource1
{
get
{
if (_itemSource1 == null)
_itemSource1 = new List<int>(){1,2,3};
return _itemSource1;
}
}
public MainWindow()
{
InitializeComponent();
}
}
As you can see in my XAML I have specified that the SelectedIndex should be 1, but with the code in my WindowBase where I am trying to inject the Canvas and the Grid this information gets lost and the SelectedIndex is at -1.
Is there a way to fix this?
I would like to keep the MainWindow as a Window and not implement it as a control and load this into some different Window inside a ContentPresenter of so.
I know this problem wouldnt exist if I declared the Canvas/Grid in the XAML directly instead of trying to inject it in codebehind, but doing this with 100+ windows and trying to maintain them if something changes is annoying.
Change your WindowBase class like that :
WindowBase
[ContentProperty("InternalContent")]
public class WindowBase : Window
{
// InternalContent
public static readonly DependencyProperty InternalContentProperty =
DependencyProperty.Register( "InternalContent", typeof(object),
typeof(WindowBase), new FrameworkPropertyMetadata(null));
public object InternalContent
{
get { return GetValue(InternalContentProperty); }
set { SetValue(InternalContentProperty, value); }
}
...
}
<Window ...>
<Grid>
...
<ContentControl IsTabStop="false"
Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=InternalContent}" />
<Canvas />
...
</Grid>
</Window>
In essence, what it does is to create a new InternalContent property that subclasses will see as the default content (thanks to ContentPropertyAttribute), and display that content with a ContentControl.
That said, there is most likely better ways to do what you're trying to do than inheritance. Using templates comes to mind. Or maybe Adorners if what you want is an "above all layer to display things" like your Canvas suggest.
I suspect your problem is the setting of Content property of your Superclass Window to a "new" grid and then inserting the original content into that grid.
Where I think the problem may be is that you are setting the variable originalContent equal to Content BUT Content is an Object meaning it's a Reference Type. Problem shown below
var originalContent = Content as FrameworkElement;
//Reference Type: originalContent POINTS AT Content;
var grid = new Grid();
Content = grid;
//Reference Type: Content POINTS AT grid vsv. originalContent now POINTS AT grid
// Now because of Pointers and Reference Types
// originalContent = grid
If you want to preserve the Original Content in your Window Base Class using the Code you've shared, you would need to clone the control and use that reference in
var originalContent = CLONE OF CONTENT.
your variable originalContent has a parent and so kannt be added to a new Frameworkelement. You must remove originalContent from the old parent before you add it to a new one.
Related
I have what I believe to be a potentially unique situation.
My ListBox items consist of the following:
StackPanel
Image
ListItem
The ListItem and Image are inserted into the StackPanel, then the StackPanel is the inserted into the ListBox for each item in the array.
Now the challenging part comes in sorting the content by the ListItem's Content (text) as it's a child of the StackPanel. Naturally, the StackPanel does not contain a Content member, so using the below code fails.
this.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("Content",
System.ComponentModel.ListSortDirection.Ascending));
So I figured, what if I set my StackPanel's data context to my ListItem, then surely it will find it.
stackPanel.DataContext = this.Items;
However, that also fails.
I'm creating my ListItems programatically in the code behind, via data that is loaded in via Json.Net.
My goal here is to sort the items from A-Z, based on the Items Content. I would prefer to keep my current implementation (creating the data programatically) as it gives me more control over the visuals. Plus, it's only about 20 lines of code.
Is it possible to use SortDescriptions when the ListItem's content is a StackPanel ?
Thank you
PS: Only started with WPF today, but have been developing WinForms apps for nearly 2 months.
The WPF way to do it would be to bind your ListBox ItemsSource to an ObservableCollection containing your items.
You would then be able to sort your observableCollection liks so :
CollectionViewSource.GetDefaultView(YourObservableCollection).SortDescriptions.Add(new SortDescription("PropertyToSort", ListSortDirection.Ascending));
Here is a small project that highlights this :
XAML :
<Window x:Class="stackPanelTest.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:stackPanelTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Image}" />
<TextBlock Text="{Binding Item.Content}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Code Behind :
public partial class MainWindow : Window
{
public ViewModel Items { get; set; } = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
ViewModel :
public class ViewModel : ObservableCollection<ListItem>
{
public ViewModel()
{
populateItems();
CollectionViewSource.GetDefaultView(this).SortDescriptions.Add(new SortDescription("Item.Content", ListSortDirection.Ascending));
}
private void populateItems()
{
addOneItem(0, "zero");
addOneItem(1, "one");
addOneItem(2, "two");
addOneItem(3, "three");
addOneItem(4, "four");
}
private void addOneItem(int img, string content)
{
ListItem item = new ListItem();
item.Image = img;
item.Item = new SomeItem { Content = content };
Add(item);
}
}
public class ListItem
{
public int Image { get; set; }
public SomeItem Item { get; set; }
}
public class SomeItem
{
public string Content { get; set; }
}
I took the liberty of renaming your "ListItem" into a "SomeItem" class because I didn't know what it was.
Then I made a "ListItem" class which is used to contain a Image/SomeItem pair (which is what your ListBox is composed of).
Also I used an int instead of an actual image but that should be easily changable.
Here's a screenshot of what I get when executing this code :
Hope this helps, good luck.
PS : if your items values are susceptible to change, don't forget to implement INotifyPropertyChanged in "SomeItem" and "ListItem", otherwise the change won't be updated in your view.
Is it possible to use SortDescriptions when the ListItem's content is a StackPanel ?
No. You will have to implement the sorting logic yourself.
There is no easy way to apply custom sorting to the ItemCollection that is returned from the Items property of the ListBox so instead of adding items to this one you could add the items to a List<StackPanel> and sort this one.
You could still create the data programatically just as before.
Here is an example for you:
Code:
public partial class MainWindow : Window
{
private List<StackPanel> _theItems = new List<StackPanel>();
public MainWindow()
{
InitializeComponent();
//create the items:
StackPanel sp1 = new StackPanel();
ListBoxItem lbi1 = new ListBoxItem() { Content = "b" };
Image img1 = new Image();
sp1.Children.Add(lbi1);
sp1.Children.Add(img1);
_theItems.Add(sp1);
StackPanel sp2 = new StackPanel();
ListBoxItem lbi2 = new ListBoxItem() { Content = "a" };
Image img2 = new Image();
sp2.Children.Add(lbi2);
sp2.Children.Add(img2);
_theItems.Add(sp2);
StackPanel sp3 = new StackPanel();
ListBoxItem lbi3 = new ListBoxItem() { Content = "c" };
Image img3 = new Image();
sp3.Children.Add(lbi3);
sp3.Children.Add(img3);
_theItems.Add(sp3);
//sort the items by the Content property of the ListBoxItem
lb.ItemsSource = _theItems.OrderBy(x => x.Children.OfType<ListBoxItem>().FirstOrDefault().Content.ToString()).ToList();
}
}
XAML:
<ListBox x:Name="lb" />
I created a user control that looks like a tile. Created another user control named TilePanel that serves as the default container of the tiles. And lastly, the very UI that looks like a Window start screen. I used RelayCommand to bind my TileCommands
Here are the codes:
Tilev2.xaml
<UserControl x:Class="MyNamespace.Tilev2"
Name="Tile"....
>
...
<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" Command="{Binding ElementName=Tile, Path=TileClickCommand}" >
</Button>
</UserControl>
Tilev2.xaml.cs
public partial class Tilev2 : UserControl
{
public Tilev2()
{
InitializeComponent();
}
//other DPs here
public ICommand TileClickCommand
{
get { return (ICommand)GetValue(TileClickCommandProperty); }
set { SetValue(TileClickCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for TileClickCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TileClickCommandProperty =
DependencyProperty.Register("TileClickCommand", typeof(ICommand), typeof(Tilev2));
}
}
Then I created a TilePanel user control as the container of the tiles
TilePanel.xaml
<UserControl x:Class="MyNamespace.TilePanel"
...
>
<Grid>
<ScrollViewer>
<ItemsControl Name="tileGroup"
ItemsSource="{Binding TileModels}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local2:Tilev2 TileText="{Binding Text}"
TileIcon="{Binding Icon}"
TileSize="{Binding Size}"
TileFontSize="{Binding FontSize}"
Background="{Binding Background}"
TileCaption="{Binding TileCaption}"
TileCaptionFontSize="{Binding TileCaptionFontSize}"
TileClickCommand="{Binding TileCommand}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</UserControl>
TilePanel.xaml.cs
public partial class TilePanel : UserControl
{
public TilePanel()
{
InitializeComponent();
DataContext = new TilePanelViewModel();
}
public TilePanelViewModel ViewModel
{
get { return (TilePanelViewModel)this.DataContext; }
}
}
My ViewModel for TilePanel
TilePanelViewModel.cs
public class TilePanelViewModel : ViewModelBase
{
private ObservableCollection _tileModels;
public ObservableCollection<TileModel> TileModels
{
get
{
if (_tileModels == null)
_tileModels = new ObservableCollection<TileModel>();
return _tileModels;
}
}
}
Then my Tile model
TileModel.cs
public class TileModel : BaseNotifyPropertyChanged
{
//other members here
ICommand tileCommand { get; set; }
//other properties here
public ICommand TileCommand
{
get { return tileCommand; }
set { tileCommand = value; NotifyPropertyChanged("TileCommand"); }
}
}
}
This is my StartScreen View where TilePanels with tiles should be displayed...
StartScreen.xaml
<UserControl x:Class="MyNamespace.StartMenu"
... >
<Grid>
<DockPanel x:Name="dockPanel1" Grid.Column="0" Grid.Row="1" Margin="50,5,2,5">
<local:TilePanel x:Name="tilePanel"></local:TilePanel>
</DockPanel>
</Grid>
</UserControl>
StartScreen.xaml.cs
public partial class WincollectStartMenu : UserControl, IView<StartMenuViewModel>
{
public WincollectStartMenu()
{
InitializeComponent();
}
public StartMenuViewModel ViewModel { get { return (DataContext as StartMenuViewModel); } }
private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
ViewModel.Tile = tilePanel.ViewModel.TileModels;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
return;
}
}
In my start screen ViewModel, I used ObservableCollection Tile
and use Tile.Add(tile); to populate my start screen with Tiles inside the TilePanel...
StartMenuViewModel.cs
TileModel tile = new TileModel() { Text = "Testing1", FontSize = 11, Size = TileSize.Medium, Background = (SolidColorBrush)new BrushConverter().ConvertFromString("#039BE5"), Tag="Something" };
tile.TileCommand = new RelayCommand(
p => Tile_TileClick(tile.Tag),
p => true
);
temp.Add(tile);
Now the problem is, if I add a new code below, tile = new TileModel() {...}
tile.TileCommand = new RelayCommand(...), even if I clicked on the first tile, my Tile_TileClick() will get the second tile's info (or the last tile inserted)...
Am I doing something wrong? Or Im doing everything wrong...?
This is not direct answer to your question, but hopefully it will give you few thoughts.
Ok, first of all, don't name your usercontrol like this:
<UserControl x:Class="MyNamespace.Tilev2" Name="Tile"/>
because the name can be easily overriden when using the usercontrol somewhere:
<local:Titlev2 Name="SomeOtherName" />
and the binding inside Tilevs with ElementName won't work: Command="{Binding ElementName=Tile, Path=TileClickCommand}"
Second, what's the point of Tilev2 usercontrol? Why don't just put the button directly to the DataTemplate inside TilePanel class?
If you need to reuse the template, you can put the template to resource dictionary.
If you need some special presentation code in the Tilev2 codebehind or you need to use the Tilev2 without viewmodel, it's better to create custom control instead of usercontrol in this case. it has much better design time support, and writing control templates it's easier (Triggers, DataTriggers, TempalteBinding, etc). If you used custom Control insead UserControl, you wouldn't have to write {Binding ElementName=Tile, Path=TileClickCommand}, or use RelativeSource, etc.
Third, it seems like you forced MVVM pattern where you can't really take advantage of it. Point of MVVM is separate application logic from presentation. But your Tile and TilePanel usercontrols are just presentation. You application logic could be in StartScreen which is concrete usage of TileName.
I would create custom controls called TilePanel (potentionally inherited from ItemsControl, Selector or ListBox) and if needed also for Tile. Both controls should not be aware of any viewmodels. There's absolutelly no need for that.
Take ListBox as an example. ListBox does not have viewmodel but can be easily used in MVVM scenarios. Just because ListBox it is not tied to any viewmodel, it can be databound to anything.
Just like ListBox creates ListBoxItems, or
Combobox creates ComboBoxItems, or
DataGrid creates DataGridRows or
GridView (in WinRT) creates GridViewRow, your TilePanel could create Tiles.
Bindings to tile specific properties, like Icon or Command could be specified in TilePanel.ItemContainerStyle orusing simillar appriach like DisplayMemberPath, resp ValueMemberPath in ListBox.
final usage could the look like:
<TilePanel ItemsSource="{Bidning ApplicationTiles}" />
or
<TilePanel>
<Tile Icon=".." Command=".." Text=".." />
<Tile Icon=".." Command=".." Text=".." />
</TilePanel>
Last, the name `TilePanel' evoked that it is some kind of panel like StackPanel, WrapPanel, etc. In other words, it is FrameworkElement inherited from Panel.
TilesView would be more suitable name for the control than TilePanel. The -View postfix is not from MVVM, it just follows naming convention -GridView, ListView...
Saw the problem...
To pass a parameter from button, I used CommandParameter so I could use it in switch-case scenario to know which button was clicked. But still, param was still null...
<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" >
</Button>
TileCommand = new MyCommand() { CanExecuteFunc = param => CanExecuteCommand(), ExecuteFunc = param => Tile_TileClick(param)}
After 2 whole damn days, I changed it:
From this:
<UserControl Name="Tile"...>
<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding Tag, ElementName=Tile}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" >
</Button>
</UserControl>
To this:
<UserControl Name="Tile"...>
<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" >
</Button>
</UserControl>
My first post does error because CommandParameter does not know where to get its DataContext so I replaced it to CommandParameter={Binding} so it will get whatever from the DataContext.
It's maybe stupid question but I just cant figure out how to do this. I have a UserControl named "ReportUserControl" and inside of it I have a Grid named "ReportGrid" . I want to remove ReportGrid from ReportUserControl children. I tried this:
ReportUserControl control = new ReportUserControl();
control.Children.Remove(...);
Problem is that there is no .Children.Remove() option for UserControl. How can i manage this?
UPDATE
I want to "send" this grid to another class and use it there. Problem is that when I send the grid and try to use it in another class I get this: "Specified element is already the logical child of another element. Disconnect it first." so I have to remove it from my UserControl but don't know how.
ReportUserControl XAML:
<UserControl x:Class="WPFReportTest.ReportUserControl "
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"
Height="29.7cm" Width="21cm">
<Grid Name="ReportGrid">
****GridContent***
</Grid>
</UserControl>
ReportClass:
public class ReportingClass
{
Grid reportGrid;
public ReportingClass(Grid tempGrid)
{
reportGrid = tempGrid;
}
public Page SetPageContent()
{
Page page = new Page();
page.Content = reportGrid;
}
}
InstanceWindow
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ReportUserControl userControl = new ReportUserControl();
ReportingClass report = new ReportingClass(userControl.ReportGrid);
}
}
This is not the full code only small part to get idea of what I'm trying to manage. Line "page.Content = reportGrid;" is where the exception happens.
UserControl has Content property (msdn).
If you want to delete content set null:
ReportUserControl control = new ReportUserControl();
control.Content = null;
Example:
public class ReportingClass
{
ReportUserControl _reportUserControl;
Grid reportGrid;
public ReportingClass(ReportUserControl reportUserControl, Grid tempGrid)
{
_reportUserControl = reportUserControl;
reportGrid = tempGrid;
}
public Page SetPageContent()
{
Page page = new Page();
// _reportUserControl is the instance of object ReportUserControl where content is reportGrid
_reportUserControl.Content = null;
page.Content = reportGrid;
return page;
}
}
Edit:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ReportUserControl userControl = new ReportUserControl();
ReportingClass report = new ReportingClass(userControl, userControl.Content as Grid);
}
}
#Stojdza, in WPF, we generally don't manipulate UI elements in that way. We manipulate data elements and declare DataTemplates to define what that data looks like. So in your case, you shouldn't literally move the Grid... just move the data and let WPF regenerate your Grid wherever you want using the same DataTemplate. It will look just the same, but it's a whole lot easier and you'll save yourself all of this trouble.
So, if you're interested in doing this the easy way, first create a class that has all of the properties required in the UserControl, let's call it Data. Now declare a DataTemplate in Application.Resources so that you can use it application-wide:
<DataTemplate DataType="{x:Type YourNamespacePrefix:Data}">
<!-- Define your Grid here -->
<DataTemplate>
Now whenever you want to display your Grid, whether in a collection, or individually, you just need to bind a property of type Data (or a collection of them) to some form of ContentControl:
public Data Data { get; set; }
...
<ContentControl Content="{Binding Data}" />
So rather than trying to copy the Grid, you just copy the data to another property of type Data and recreate the Grid with the single line above. See the Data Templating Overview page on MSDN for more information on DataTemplates.
I use MVVM. I have a ViewA with a Grid in xaml and a ViewModelA. In the ViewModelA I have a Method that looks like this which is called on a Button click:
public void ButtonClickMethod()
{
ViewB viewB = new ViewB();
viewB.DataContext = new ViewBViewModel();
}
How do I add all the created viewBs into the Grid on my ViewA, so I can see them there? Or is my solution wrong in general maybe?
EDIT :
I now used a ObservableCollection<ViewB> in my ViewModelA which notifies on changes in my ViewA xaml at
<ItemsControl ItemsSource="{Binding ObservableCollection<ViewB>}" />
The only problem is the ViewBs shown in the ItemControl should be dragable. Therefore I used expression blend's
Interaction.GetBehaviors(ViewB).Add(new MouseDragElementBehavior() { });
when i create a new ViewB. But it doesn't work.
EDIT2 :
I tried this solution but it doesn't work for me :( Using MouseDragElementBehavior with an ItemsControl and Canvas
Add your Grid an ContentControl and bind it content to your second view.
Here an example with an UserControl.
<ContentControl Grid.Column="0" Margin="5" Content="{Binding SecondView}"/>
Code im ViewModel:
private UserControl secondView;
public UserControl SecondView
{
get
{
return secondView;
}
set
{
this.SetProperty(ref secondView,value);
}
}
I'm new to WPF and I'm trying to figure out how data binding works, but I'm not having much luck.
I'm trying to start with something simple - binding the contents of a text box to a string variable in my program.
I read lots and lots of pages of MSDN documentation about data binding, XML namespaces, markup extensions, resources, dependency properties and whatnot, and I'm still not able to get it to work.
Here's my MainWindow.xaml:
<Window x:Class="WpfTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WpfTest"
Title="MainWindow">
<Grid>
<Grid.Resources>
<c:Foo x:Key="MyFoo"/>
</Grid.Resources>
<TextBox Width="100" Height="28"
Text="{Binding Source=MyFoo,
Path=BarProperty,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
And my MainWindow.xaml.cs:
namespace WpfTest
{
public class Foo : DependencyObject
{
public static readonly DependencyProperty BarProperty = DependencyProperty.Register("Bar", typeof(String), typeof(Foo));
public String Bar
{
get { return (String)GetValue(BarProperty); }
set { SetValue(BarProperty, value); }
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyFoo = new Foo { Bar = "hello" };
}
public Foo MyFoo { get; set; }
}
}
I would expect the text box to show "hello" when the program starts up, but it is empty.
Can someone tell me what I am doing wrong?
You need to set the DataContext of your Window to itself.
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
MyFoo = new Foo { Bar = "hello" };
}
This tells WPF to look for bindings within your class.
Every control can set a DataContext which says "when I bind, I want to bind to a property on this specific instance... This is inherited, so if you set the DataContext of the MainWindow to itself, all controls inside of MainWindow will bind to properties on the MainWindow.
You need to specify the source. Either:
Give the window a name like Name="mywin", alter your binding witn ElementName="myWin"
Or set the window DataContext like:
DataContext="{Binding ElementName="myWin"} - you can also use a RelativeSource if you don't want the name I just couldn't post it untested - Bindings tend to require testing as you also noticed :)
This might help:
http://blogs.msdn.com/b/wpfsdk/archive/2006/10/19/wpf-basic-data-binding-faq.aspx