DataContext in WPF throws an exception - wpf

<Window x:Class="WpfApplication1.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>
<TextBox Name="myTxt" Text="{Binding}" />
</Grid>
</Window>
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = "fdfsfds";
}
}
}
I wonder why this code isn't working? It throws an exception. What should I do to bind textBox?

The default Binding for TextBox.Text property - is TwoWay
"Two-way binding requires Path or XPath."
So, you can use OneWay Binding:
<Grid>
<TextBox Name="myTxt" Text="{Binding Mode=OneWay}" />
</Grid>

If you still want TwoWay binding you may use this code:
<TextBox Name="myTxt" Text="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}" />

Related

binding to an property of built-in control within custom usercontrol from another usercontrol

Due to the origin MainWindow.xaml was too large, really difficult to maintain. so I separate them into several User Controls. However I encountered the following issue, one control in UserControl_2 is refer to ListView's selection within UserControl_1. I tried to alter the binding, but none of them working as expected. Any idea how to binding to another User Control correctly?
MainWindow.xaml:
<Window x:Class="MyApp.MainWindow" ...>
<Grid>
<view:UserControl_1/>
<view:UserControl_2/>
</Grid>
</Window>
UserControl_1.xaml:
<UserControl x:Class="MyApp.views.UserControl_1 ...>
<Grid>
<ListView x:Name="MyListView" />
</Grid>
</UserControl>
UserControl_2.xaml
<UserControl x:Class="MyApp.views.UserControl_2 ...>
<Grid>
<Button Content="Test"
Command="TestCommand"
CommandParameter="{Binding Path=MyListView.SelectedIndex,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl_1}}}"
</Grid>
</UserControl>
Create a view model class and set this one as the DataContext of the parent window:
public class ViewModel
{
private int _selectedIndex;
public int SelectedIndex
{
get { return _selectedIndex; }
set { _selectedIndex = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml:
<Window x:Class="MyApp.MainWindow" ...>
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<view:UserControl_1/>
<view:UserControl_2/>
</Grid>
</Window>
You can then bind the ListView and the Button in the user controls to the same source property.
UserControl_1.xaml:
<UserControl x:Class="MyApp.views.UserControl_1 ...>
<Grid>
<ListView x:Name="MyListView" SelectedIndex="{Binding DataContext.SelectedIndex, RelativeSource={RelativeSource AncestorType=Window}}" />
</Grid>
</UserControl>
UserControl_2.xaml:
<UserControl x:Class="MyApp.views.UserControl_2 ...>
<Grid>
<Button Content="Test"
Command="TestCommand"
CommandParameter="{Binding Path=DataContext.SelectedIndex,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
</Grid>
</UserControl>

Binding StringFormat to a binding

Can I bind StringFormat inside a Binding to another Binding?
Text="{Binding DeficitDollars, StringFormat=\{0:E6\}}"
Text="{Binding GNP, StringFormat={Binding LocalCurrencyFormat}}"
You can't use a Binding for StringFormat. As the exception tells you if you try it:
A 'Binding' can only be set on a DependencyProperty of a
DependencyObject
StringFormat is not a DependencyProperty and Binding is not a DependencyObject.
You can do these two things though.
Setting it to a resource.
You can define your different string formats in App.xaml in the Resources so they'll be reachable in the whole application:
<system:String x:Key="LocalCurrencyFormat">{0:C}</system:String>
system is xmlns:system="clr-namespace:System;assembly=mscorlib"
And then you can do:
<TextBlock Text="{Binding MyDouble, StringFormat={StaticResource LocalCurrencyFormat}}" />
Setting it to a static property of a class.
You can have a class with all your different string formats:
public static class StringFormats
{
public static string LocalCurrencyFormat
{
get { return "{0:C}"; }
}
}
And use it in the Binding the following way:
<TextBlock Text="{Binding MyDouble, StringFormat={x:Static local:StringFormats.LocalCurrencyFormat}}" />
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Width="500" Height="500">
<Window.Resources>
<sys:String x:Key="LocalCurrencyFormat">Total: {0:C}</sys:String>
</Window.Resources>
<StackPanel>
<TextBlock Text="{Binding DeficitDollars, StringFormat=\{0:E6\}}"></TextBlock>
<TextBlock Text="{Binding Path=GNP, StringFormat={StaticResource LocalCurrencyFormat}}" />
</StackPanel>
</Window>

Application Settings binding to instance specific UserControl

I have three controls in my application of type MyUserControl. MyUserControl contains a Label which I want to bind to a string located in the Application Settings. Each of the three instances of MyUserControl has its own string in the Application Settings named Description1, Description2 and Description3 respectively.
The problem is that I cannot set the path of the binding in the UserControl to the name of the string located in the Application Settings because then every instance of MyUserControl would bind to the same string.
I have managed to get something working but as I have learned while working with WPF is that the solution I come up with is never the best way to do things :), so I was wondering if there is a better way to do this? Below is the relevant code I am using now:
MyUserControl.xaml.cs
public partial class MyUserControl : UserControl
{
public static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(MyUserControl));
public string Description { get { return (string)GetValue(DescriptionProperty); } set { SetValue(DescriptionProperty, value); } }
}
MyUserControl.xaml
<UserControl x:Class="MyApplication.MyUserControl"
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:local="clr-namespace:MyApplication"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Name="myUserControl">
...
<Label Content="{Binding ElementName=myUserControl, Path=Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,6" />
MainWindow.xaml
xmlns:properties="clr-namespace:MyApplication.Properties"
...
<local:MyUserControl Description="{Binding Source={x:Static properties:Settings.Default}, Path=Description1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="myUserControl1" DataContext="{Binding ElementName=MainWindow, Path=Params}" />
<local:MyUserControl Description="{Binding Source={x:Static properties:Settings.Default}, Path=Description2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="myUserControl2" DataContext="{Binding ElementName=MainWindow, Path=Params}" />

WPF -- Anyone know why I can't get this binding to reference?

<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3"
DataContext="{Binding Mode=OneWay, Source={StaticResource local:oPatients}}">
I'm getting StaticResource reference 'local:oPatients' was not found.
Here is the codebehind:
public partial class MainWindow : Window
{
ListBox _activeListBox;
clsPatients oPatients;
public MainWindow()
{
oPatients = new clsPatients(true);
...
To be able to address the object as a StaticResource, it needs to be in a resource dictionary. However, since you're creating the object in MainWindow's constructor, you can set the DataContext in the code-behind like so.
oPatients = new clsPatients(true);
stkWaitingPatients.DataContext = oPatients;
And then change the Binding to this:
{Binding Mode=OneWay}
This is an ok practice if you're not going to be changing the DataContext again, otherwise you'd want a more flexible solution.
Edit: You mentioned ObjectDataProvider in your comment. Here's how you'd do that. First, add an xmlns:sys to the Window for the System namespace (I'm assuming you already have one for xmlns:local):
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Then you can add an ObjectDataProvider to your resource dictionary like this:
<Window.Resources>
<ObjectDataProvider
x:Key="bindingPatients"
ObjectType="{x:Type local:clsPatients}">
<ObjectDataProvider.ConstructorParameters>
<sys:Boolean>True</sys:Boolean>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</Window.Resources>
And refer to it in a Binding with the StaticResource markup like this, using the same string we specified in the x:Key attached property we gave it in the dictionary:
{Binding Source={StaticResouce bindingPatients}, Mode=OneWay}
Edit 2: Ok, you posted more code in your answer, and now I know why it's throwing an exception during the constructor. You're attempting to do this...
lstWaitingPatients.DataContext = oPatients;
... but lstWaitingPatients doesn't actually exist until after this.InitializeComponent() finishes. InitializeComponent() loads the XAML and does a bunch of other things. Unless you really need to do something before all of that, put custom startup code after the call to InitalizeComponent() or in an event handler for Window's Loaded event.
The following sets the ItemsSource in Code Behind and correctly handles the DataBinding:
public partial class MainWindow : Window
{
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//assuming oPatients implements IEnumerable
this.lstWaitingPatients.ItemsSource = oPatients;
And the XAML:
<ListBox x:Name="lstWaitingPatients"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291"
ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
Now, I can't get this to work...I get a general Windows startup error.
Here is the codebehind with the Initializer and the class being instantiated:
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
lstWaitingPatients.DataContext = oPatients;
this.InitializeComponent();
Here's the top of my XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Orista_Charting"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
x:Class="Orista_Charting.MainWindow"
x:Name="windowMain"
Title="Orista Chart"
Width="1024" Height="768" Topmost="True" WindowStartupLocation="CenterScreen" Activated="MainWindow_Activated" >
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/ButtonStyles.xaml"/>
<ResourceDictionary Source="Resources/OtherResources.xaml"/>
<ResourceDictionary Source="Resources/TextBlockStyles.xaml"/>
<ResourceDictionary Source="Resources/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
Here's the pertinent XAML, as you see, I went ahead and moved the DataContext down to the ListBox from the StackPanel. This doesn't run, but it does render in Design View (however, with no data present in the ListBox):
<!-- Waiting Patients List -->
<Border BorderThickness="1,1,1,1" BorderBrush="#FF000000" Padding="10,10,10,10"
CornerRadius="10,10,10,10" Background="#FFFFFFFF" Margin="15.245,187.043,0,41.957" HorizontalAlignment="Left" >
<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Waiting Patients:" VerticalAlignment="Center" FontSize="21.333" Margin="0,0,0,20"/>
<TextBlock HorizontalAlignment="Right" Margin="0,0,38.245,0" Width="139" Height="16"
Text="Minutes Waiting" TextWrapping="Wrap" Foreground="#FF9C2525" FontWeight="Bold" VerticalAlignment="Bottom"
TextAlignment="Right"/>
<!-- Too be implemented, this is the wait animation -->
<!--<Image x:Name="PollGif" Visibility="{Binding Loading}"
HorizontalAlignment="Left" Margin="100,0,0,0" Width="42.5" Height="42.5"
Source="Images/loading-gif-animation.gif" Stretch="Fill"/>-->
</StackPanel>
<ListBox x:Name="lstWaitingPatients"
DataContext="{Binding Mode=OneWay}" ItemsSource="{Binding Mode=OneWay}"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291" ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
</StackPanel>
</Border>
Ok, but if I just take comment out the assigment line in the codebehind, it does run (albeit with no data in the listbox):
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//lstWaitingPatients.DataContext = oPatients;
THANKS!

Why can I bind to ListBox but not to DataGrid in WPF?

I set up LINQ-to-SQL / NorthWind in WPF.
The ListBox shows data but the DataGrid doesn't (no errors, just doesn't display anything).
I referenced WPFToolkit.dll.
Why is the DataGrid not displaying the data that ListBox can?
XAML:
<Window x:Class="TestLinq343.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="ShowCustomer">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CategoryID}"/>
<TextBlock Text=": "/>
<TextBlock Text="{Binding ProductName}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<dg:DataGrid x:Name="TheDataGrid" AutoGenerateColumns="True"></dg:DataGrid>
<ListBox x:Name="TheListBox" ItemTemplate="{StaticResource ShowCustomer}"/>
</Grid>
</Window>
code behind:
using System.Linq;
using System.Windows;
using TestLinq343.Model;
using Microsoft.Windows.Controls;
namespace TestLinq343
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
NorthwindDataContext db = new NorthwindDataContext();
var sortedProducts =
from p in db.Products
orderby p.UnitsInStock descending
select p;
TheDataGrid.ItemsSource = sortedProducts;
TheListBox.ItemsSource = sortedProducts;
}
}
}
It was just a XAML issue, this fixes it:
<ScrollViewer>
<StackPanel>
<dg:DataGrid x:Name="TheDataGrid"/>
<ListView x:Name="TheListView" ItemTemplate="{StaticResource ShowCustomer}"/>
</StackPanel>
</ScrollViewer>
maybe because you did not specifiy datagrid columns. try setting the datagrids AutoGenerateColumns property to true.

Resources