binding a combobox to different DataContext - binding

The combobox items are taken from one table, one field on which binding is made. After I saved in the database the selected item in another table, I want that the selected item to be the one which was saved. But the selected item is lost. So, my question is: can I bind the combobox to two DataContexts or maybe another solution ?.
To give an example to be more clear: the combobox items are predefined values taken from a datasource and the value selected must be saved and shown on the interface. So, from what I can see must be a binding to the predefined values and also a binding to the value saved to make a connection to the selected item.
Any suggestion?

Ioana, I don't seem to get what you're aiming at..
if you take this xaml:
<Window x:Class="WpfApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Height="300"
Width="300">
<StackPanel>
<TextBox Text="{Binding Path=SelectedText, Mode=TwoWay}"
Width="200"/>
<ComboBox Width="200"
VerticalAlignment="Center"
HorizontalAlignment="Center"
SelectedItem="{Binding Path=SelectedText, Mode=TwoWay}"
ItemsSource="{Binding Path=Texts, Mode=OneWay}">
</ComboBox>
</StackPanel>
</Window>
and this codebehind:
public partial class Window1 : INotifyPropertyChanged
{
public Window1()
{
InitializeComponent();
this.Texts = new List<string>(new[] {"foo","bar"});
this.DataContext = this;
}
private ObservableCollection<string> texts;
public ObservableCollection<string> Texts
{
get
{
return texts;
}
set
{
texts = value;
if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("Texts"));
}
}
private string selectedText;
public string SelectedText
{
get
{
return selectedText;
}
set
{
selectedText = value;
if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("SelectedText"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
You do have the Items and the selectedValue databound.
Notice the INotifyPropertyChanged.
Is this what you're trying to achieve ?

Related

How to set a binding to an element that was not instantiated in XAML code

I´m relative new to coding and I´m working on a little project. This is what I´m trying to do:
I defined a class "MyObject" with two properties:
namespace WpfApplication2
{
public class MyObject
{
public string Property1 { get; set; }
public int Property2 { get; set; }
public MyObject() : this("", 0)
{
}
public MyObject(string p1, int p2)
{
Property1 = p1;
Property2 = p2;
}
}
}
...then instantiated two objects of this class in code:
namespace WpfApplication2
{
public partial class MainWindow : Window
{
public List<MyObject> listOfMyObject { get; set; }
public MyObject myObj1 { get; set; }
public MyObject myObj2 { get; set; }
public MainWindow()
{
InitializeComponent();
listOfMyObject = new List<MyObject>();
myObj1 = new MyObject("Hello", 1);
myObj2 = new MyObject("Bye", 2);
listOfMyObject.Add(myObj1);
listOfMyObject.Add(myObj2);
}
}
}
Now I want to bind each property of the two MyObject objects to the Content Property of a Label object. So there should be four Label objects:
- Label1 should display the value of Property1 of myObj1
- Label2 should display the value of Property2 of myObj1
- Label3 should display the value of Property1 of myObj2
- Label4 should display the value of Property2 of myObj2
I tried it this way:
<Window x:Class="WpfApplication2.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:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Name="mywin">
<Grid>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<StackPanel>
<Label Name="Label1" Content="{Binding ElementName=myObj1, Path=Property1}"/>
<Label Name="Label2" Content="{Binding ElementName=myObj1, Path=Property2}"/>
</StackPanel>
<StackPanel>
<Label Name="Label3" Content="{Binding ElementName=myObj2, Path=Property1}"/>
<Label Name="Label4" Content="{Binding ElementName=myObj2, Path=Property2}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
... but it doesn´t work. Please help me to understand how to use the Binding correctly!
Greetings
// Even if I already figured out how to solve my problem, I would be pleased, // if someone could answer to the question at the end of this post!
Okay, now I figured out (with a little help from a friend), how to fix my problem:
I set the DataContext property of the MainWindow object, that contained the Label objects to itself by doing this:
mywin.DataContext = this;
So the code looks like this now:
public partial class MainWindow : Window
{
public List<MyObject> listOfMyObject { get; set; }
public MyObject myObj1 { get; set; }
public MyObject myObj2 { get; set; }
public MainWindow()
{
InitializeComponent();
listOfMyObject = new List<MyObject>();
myObj1 = new MyObject("Hello", 1);
myObj2 = new MyObject("Bye", 2);
listOfMyObject.Add(myObj1);
listOfMyObject.Add(myObj2);
// I added this code
mywin.DataContext = this;
}
}
And then I set the binding to the Content property of the four Label objects by doing this:
<Label Name="Label1" Content="{Binding Path=myObj1.Property1}" />
So my whole XAML code looks like this now:
<Window x:Class="WpfApplication2.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:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Name="mywin">
<Grid>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<StackPanel>
<Label Name="Label1" Content="{Binding Path=myObj1.Property1}" />
<Label Name="Label2" Content="{Binding Path=myObj1.Property2}" />
</StackPanel>
<StackPanel>
<Label Name="Label3" Content="{Binding Path=myObj2.Property1}" />
<Label Name="Label4" Content="{Binding Path=myObj2.Property2}" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
NEW Question:
Now I would like to understand, why it doesn´t work the way I tried in the first way...
<Label Name="Label1" Content="{Binding ElementName=myObj1, Path=Property1}"/>
... when this would work:
<Label Name="Label1" Content="{Binding ElementName=Label2, Path=Content}"/>
<Label Name="Label2" Content="Hello">
The XAML code, in which the Label objects are instantiated and the C# code, in which the MyObject objects are instantiated, are both partial classes that belong together. In addition to that the MyObject objects myObj1 and myObj2 are properties of this class. So I thought that the Label-Elements in the XAML code should "know" about the MyObject objects myObj1 and myObj2 and therefore be able to reference them as source elements in the ElementName property of the Binding object. Thinking this way, I thought I must only set the Path property of the Binding object to the Property which value the Label object should display.
Can you help me to understand, where my idea of Binding is wrong? Thank you!

Silverlight 4 Tooltip Visibility Binding

I'm trying to bind tooltip visibility in XAML and I'm running into a confusing problem where my visibility binding works fine on other controls but not on the tooltip.
I have a form with a submit button that is disabled when the required fields haven't been entered. When the button is disabled I want a tooltip on it with a relevant message. When it's enabled there is no need for the tooltip. To accomplish this I place the button in a transparent border and set the tooltip on the border since a tooltip on the button itself will never show when it's disabled. However, visibility binding to the tooltip fails and it seems I can only change the visibility in code-behind. I can use the exact same binding on visibility for various controls (in the example below I use it on a TextBlock as well). If I apply the exact same binding in code-behind, it works fine. Why doesn't this work in XAML?
XAML
<UserControl.Resources>
<local:VisibilityConverter x:Key="visibilityConverter"/>
<local:VisibilityConverter x:Key="reversedVisibilityConverter" IsReversed="True"/>
</UserControl.Resources>
<StackPanel Background="White"
Width="310">
<TextBlock Text="Using XAML binding for tooltip visibility..."
FontWeight="Bold"/>
<CheckBox x:Name="cbEnable"
Content="Enable Submit Button"/>
<Border Background="Transparent"
Margin="0,10,0,0">
<ToolTipService.ToolTip>
<!-- This has the same binding as the 2nd TextBlock below, it should be visible when cbEnable is NOT checked and collapsed when it is checked -->
<ToolTip Content="Submit Button Is Disabled"
Visibility="{Binding IsChecked, ElementName=cbEnable, Converter={StaticResource reversedVisibilityConverter}}"/>
</ToolTipService.ToolTip>
<Button Content="Submit"
IsEnabled="{Binding IsChecked, ElementName=cbEnable}"/>
</Border>
<!-- This TextBlock is visibile when cbEnable is checked -->
<TextBlock Text="Submit Button is enabled"
Visibility="{Binding IsChecked, ElementName=cbEnable, Converter={StaticResource visibilityConverter}}"/>
<!-- This TextBlock is visibile when cbEnable is NOT checked (same as ToolTip binding above -->
<TextBlock Text="Submit Button is disabled"
Visibility="{Binding IsChecked, ElementName=cbEnable, Converter={StaticResource reversedVisibilityConverter}}"/>
<TextBlock Text="Using code-behind binding for tooltip visibility..."
FontWeight="Bold"
Margin="0,20,0,0"/>
<CheckBox x:Name="cbEnable2"
Content="Enable Submit Button"/>
<Border Background="Transparent"
Margin="0,10,0,0">
<ToolTipService.ToolTip>
<ToolTip x:Name="toolTip2"
Content="Submit Button 2 Is Disabled"/>
</ToolTipService.ToolTip>
<Button Content="Submit 2"
IsEnabled="{Binding IsChecked, ElementName=cbEnable2}"/>
</Border>
<TextBlock Text="Submit Button 2 is enabled"
Visibility="{Binding IsChecked, ElementName=cbEnable2, Converter={StaticResource visibilityConverter}}"/>
<TextBlock Text="Submit Button 2 is disabled"
Visibility="{Binding IsChecked, ElementName=cbEnable2, Converter={StaticResource reversedVisibilityConverter}}"/>
</StackPanel>
Code-behind:
public partial class MainPage : UserControl {
public MainPage() {
InitializeComponent();
toolTip2.SetBinding(ToolTip.VisibilityProperty, new System.Windows.Data.Binding("IsChecked") {
Source = cbEnable2,
Converter = new VisibilityConverter() { IsReversed = true }
});
}
}
VisibilityConverter:
public class VisibilityConverter : IValueConverter {
public bool IsReversed { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
bool isVisible = System.Convert.ToBoolean(value, CultureInfo.InvariantCulture);
if (IsReversed) {
isVisible = !isVisible;
}
return (isVisible ? Visibility.Visible : Visibility.Collapsed);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
bool isVisible = ((Visibility)value == Visibility.Visible);
if (IsReversed) {
isVisible = !isVisible;
}
return isVisible;
}
}
Well, I don't know why this fixed it but here is what I did: I renamed the property in my ViewModel. Yep, I know. Seems ridiculous. I changed the property from IsWaitingVisible to WaitingVisibility. I got the idea because I changed the ViewModel property from a bool to text and then bound it to a temporary, visible TextBlock. It would not show the value the same way a different property in the same ViewModel would. That was just crazy so I renamed the property to something else and voila! The text started appearing in the UI. Then, I reconnected the visibility property of the grid (and changed my VisibilityConverter to work with strings instead of bool) and everything worked.
I guess for the purposes of science, I should change the property name back to IsWaitingVisible and see if it breaks. If so, I will have to conclude that this is a hard bug in SL 5.
This kind of flakiness just scares me when I think about building reliable apps in Silverlight.

WP7 - Call bound number on clicking Listbox item

So I have a list of objects containing a name and number bound to a listbox in XAML. The listbox displays the number fine, but I want it to call the phone number using phonecall on click. Here is the onlick code:
private void taxiListItem_Click(object sender, RoutedEventArgs e)
{
Microsoft.Phone.Tasks.PhoneCallTask phonecall = new Microsoft.Phone.Tasks.PhoneCallTask();
phonecall.PhoneNumber = "213";
phonecall.Show();
}
and here is where I define the TaxiCompany object that populates the list.
public class TaxiCompany {
public String CoName { get; set; }
public String Phone { get; set; }
public TaxiCompany(String coname, String phone) {
this.CoName = coname;
this.Phone = phone;
}
}
The phone call works fine when I hardcode the number . Now, when I set phonecall.Phonenumber = sender.getPhone() or e.Phone() or any variant of the two, its marked as an undefined method. Am I doing something fundamentally wrong here? I assume object sender or e is the list item being clicked on.
Note: the listbox in the XAML displays both the phone number and address just fine
<Button Click="taxiListItem_Click" Width ="436" Height="120">
<Button.Content>
<StackPanel Orientation="Vertical" Height=" 80">
<StackPanel Orientation="Horizontal" Height="40" Width="436">
<TextBlock Width="436" FontSize="30" Text= "{Binding CoName}" Height="40"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Height="40" Width="436">
<TextBlock Name ="PhoneNo" Width="300" FontSize="22" Text= "{Binding Phone}" Height="40"/>
</StackPanel>
</StackPanel>
</Button.Content>
</Button>
First time working with C# / Silverlight so any help would be apprecaited.
Try this. The listbox shows the taxi companies and selecting an item in the list causes the phone numbe rot try and be dialed.
Notice that it's not necessary to add buttons to the list and that it's necessary to cast the variables passed to the selection event. (Could also cast the sender to a listbox and then cast the selected item.)
xaml:
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding TaxiCompanies}" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Height=" 80">
<TextBlock Width="436" FontSize="30" Text= "{Binding CoName}" Height="40"/>
<TextBlock Name ="PhoneNo" Width="300" FontSize="22" Text= "{Binding Phone}" Height="40"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
cs:
// class used for example. Another name would be more appropriate
public class ViewModel
{
public ObservableCollection<TaxiCompany> TaxiCompanies { get; private set; }
public ViewModel()
{
TaxiCompanies = new ObservableCollection<TaxiCompany>();
TaxiCompanies.Add(new TaxiCompany("AAA Cabs", "123-456-789"));
TaxiCompanies.Add(new TaxiCompany("BBB Taxis", "111234329"));
TaxiCompanies.Add(new TaxiCompany("CCC Cars", "98765432"));
}
}
public MainPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
DataContext = new ViewModel();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
private void MainListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var phonecall = new PhoneCallTask();
phonecall.PhoneNumber = ((TaxiCompany)(((object[])(e.AddedItems))[0])).Phone;
phonecall.Show();
// Reset selected index to -1 (no selection)
MainListBox.SelectedIndex = -1;
}

Enable button when text.Length > 0 in WPF only works when textBox loses focus - why?

Simple WPF question
I have a text Box and an OK Button and I am using a MVVM pattern.
All I want to do is enable OK button only when textBox.Length > 0.
But what I get is only when the textBox loses focus the button is enabled.
Am I missing something?
I have bound the datacontext to the viewmodel and done the following.
Why does it not work?
Thanks for your help
XAML
<TextBox Grid.Column="1"
Grid.Row="2"
Margin="4"
Name="txtName" Text="{Binding Path=Name}"/>
<Button x:Name="btnOK"
MinWidth="70" Padding="3.5"
Margin="3.5"
Grid.Column="1"
Content="OK"
Click="OnOk"
Command="{Binding Path=OKCommand}"
VerticalAlignment="Center"
HorizontalAlignment="Left" IsDefault="True" />
In view Model
public class TestViewModel : ViewModelBase
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
private RelayCommand _OkCommand;
public ICommand OKCommand
{
get
{
return _OkCommand ?? (_OkCommand = new RelayCommand(x => Execute(), x => CanExecute));
}
}
private bool CanExecute
{
get
{
return !string.IsNullOrEmpty(Name);
}
}
private void Execute()
{
//do something here
}
Exchange this line:
Text="{Binding Path=Name}"
With
Text="{Binding Path=Name,UpdateSourceTrigger=PropertyChanged}"
That way the binding will update everytime the Text-property changes. Default for the Text Property of TextBox is LostFocus.

Dependency Properties and Data Context in Silverlight 3

I am working with Silverlight 3 beta, and am having an issue. I have a page that has a user control that I worte on it. The user control has a dependency property on it. If the user control does not define a data context (hence using the parent's data context), all works well. But if the user control has its own data context, the dependency property's OnPropertyChanged method never gets called.
Here is a sample:
My Main Page:
<UserControl x:Class="TestDepProp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:app="clr-namespace:TestDepProp"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="100">
<Grid x:Name="LayoutRoot" Background="White">
<Border BorderBrush="Blue" BorderThickness="3" CornerRadius="3">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<TextBlock Text="Enter text here:" />
<TextBox x:Name="entryBlock" Text="{Binding Data, Mode=TwoWay}"/>
<Button Content="Go!" Click="Button_Click" />
<TextBlock Text="{Binding Data}" />
</StackPanel>
<Border BorderBrush="Blue" BorderThickness="3" CornerRadius="3" Margin="5">
<app:TestControl PropOnControl="{Binding Data}" />
</Border>
</StackPanel>
</Border>
</Grid>
</UserControl>
Main Page code:
using System.Windows;
using System.Windows.Controls;
namespace TestDepProp
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
MainPageData data = new MainPageData();
this.DataContext = data;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int i = 1;
i++;
}
}
}
Main Page's data context:
using System.ComponentModel;
namespace TestDepProp
{
public class MainPageData:INotifyPropertyChanged
{
string _data;
public string Data
{
get
{
return _data;
}
set
{
_data = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Data"));
}
}
public MainPageData()
{
Data = "Initial Value";
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
Control XAML:
<UserControl x:Class="TestDepProp.TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:app="clr-namespace:TestDepProp"
>
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Vertical" Margin="10" >
<TextBlock Text="This should change:" />
<TextBlock x:Name="ControlValue" Text="Not Set" />
</StackPanel>
</Grid>
</UserControl>
Contol code:
using System.Windows;
using System.Windows.Controls;
namespace TestDepProp
{
public partial class TestControl : UserControl
{
public TestControl()
{
InitializeComponent();
// Comment out next line for DP to work
DataContext = new MyDataContext();
}
#region PropOnControl Dependency Property
public string PropOnControl
{
get { return (string)GetValue(PropOnControlProperty); }
set { SetValue(PropOnControlProperty, value); }
}
public static readonly DependencyProperty PropOnControlProperty =
DependencyProperty.Register("PropOnControl", typeof(string), typeof(TestControl), new PropertyMetadata(OnPropOnControlPropertyChanged));
private static void OnPropOnControlPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TestControl _TestControl = d as TestControl;
if (_TestControl != null)
{
_TestControl.ControlValue.Text = e.NewValue.ToString();
}
}
#endregion PropOnControl Dependency Property
}
}
Control's data context:
using System.ComponentModel;
namespace TestDepProp
{
public class MyDataContext : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
To try it out, type something in the text box, and hit the Go button. Comment out the data context in the controls code to see that it starts to work.
Hope someone has an idea as to what is going on.
The user control's datacontext does not have a Data property.
Because it doesn't have a data property the databinding returns null which is already the default value so the property change never fires.

Resources