I stuck in a Problem:
I have a PopUp window, its DataContext points to an object which holds a reference to a ListBox (reftolistbox).
I managed to create a working binding with this codebehind code:
private void ID_Loaded(object sender, RoutedEventArgs e)
{
Binding myBinding = new Binding("id");
myBinding.Source = ((myclass)DataContext).reftolistbox;
myBinding.Path = new System.Windows.PropertyPath("SelectedItem.Name");
BindingOperations.SetBinding(ID, ComboBox.TextProperty, myBinding);
}
I want to replace the above code with a XAML Solution,
here is a list i tried but no one worked.
<Combobox ...
Text="{Binding Source=DataContext.reftolistbox, Path=SelectedItem.Name }"
Text="{Binding reftolistbox.SelectedItem.Name }"
Text="{Binding Path=DataContext.reftolistbox.SelectedItem.Name}"
Need a XAML Solution, what am I doing wrong?
XAML only binds to properties
DataContext:
{ public ListBox reftolistbox { get; set; } }
working binding in XAML:
Text="{Binding reftolistbox.SelectedItem.Name }"
Related
I try to use binding to display Hi in the Text content.
However, when clicking the button, it doesn't work.
Could someone help me to solve the problem?
Thanks.
1.XAML CODE :
<Window x:Class="Wpftest.binding.Window0"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window0" Height="300" Width="300">
<Grid>
<TextBox x:Name="textBox2" VerticalAlignment="Top" Width="168"
Text="{Binding Source= stu, Path= Name, Mode=TwoWay}"/>
</Grid>
</Window>
2.Class :
namespace Wpftest.binding.Model
{
public class student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return name; }
set { name = value;
if(this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new
PropertyChangedEventArgs("Name"));
}
}
}
}
}
3.XAML.cs:
namespace Wpftest.binding
{
public partial class Window0 : Window
{
student stu;
public Window0()
{
InitializeComponent();
stu = new student();
}
private void button_Click(object sender, RoutedEventArgs e)
{
stu.Name += "Hi!";
}
}
}
There are many ways to achieve what you need; the correct method depends very much on what style of application you want to create. I'll demonstrate two methods that will require minimal changes from your supplied example:
Method 1
Set the DataContext to stu and bind to the Name property.
XAML.cs
private student stu;
public Window0()
{
InitializeComponent();
stu = new student();
DataContext = stu;
}
XAML code
<TextBox Text="{Binding Path=Name, Mode=TwoWay}"/>
Method 2
Generally you will set the DataContext to some object other than the Window (e.g. the ViewModel if you are following the MVVM pattern), but sometimes you may need to bind a control to some property of the Window. In this case the DataContext can't be used, but you can still bind to a property of the Window by using RelativeSource. See below:
XAML.cs
// note this must be a property, not a field
public student stu { get; set; }
public Window0()
{
InitializeComponent();
stu = new student();
}
XAML code
<TextBox Text="{Binding Path=stu.Name, Mode=TwoWay,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
Hint: if you are having trouble with WPF data binding, then it often helps to look at the debugger output window to see the binding trace messages. And debugging can be further enhanced by adding this namespace to the Window element
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
and then setting the TraceLevel e.g.
<TextBox Text="{Binding Source=stu, diag:PresentationTraceSources.TraceLevel=High}"/>
Basically you need to set DataContext property to your Window.
For example:
public MainWindow()
{
DataContext=new YourViewModel();
}
DataContext of Window is a way to communicate between View(XAML) and ViewModel(C# code)
In addition, you can add DataContext in xaml:
<Window.DataContext>
<local:YourViewModel/>
</Window.DataContext>
Also, instead of handling Click event, you should use Command property of Button. Example can be seen here.
In setting up data binding for Observable Collection , under the following context: Implementing CollectionChanged Handler in XAML with WPF all bindings are working correctly, but I'm finding that in addition to changing the Property defined by ItemsSource within the ListBox, I am having to manually update the UI's visual container with code similar to:
XAML:
<Grid DataContext="{Binding ElementName=PollPublicStockMainWindow}">
<ListBox Height="132" HorizontalAlignment="Left" Name="lbFiles"
VerticalAlignment="Top" Width="167"
Margin="{StaticResource ConsistemtMargins}"
ItemsSource="{Binding LbItems}">
<ListBox.InputBindings>
<KeyBinding Key="Delete" Command="local:MainWindow.DeleteEntry"/>
</ListBox.InputBindings>
</ListBox>
</Grid>
CodeBehind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LbItems = new ObservableCollection<string>();
LbItems.CollectionChanged += lbFiles_CollectionChanged;
}
private void lbFiles_CollectionChanged(object sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
MemoryPersistentStorageBridge memBridge = GetPersistentStorageBridge;
List<string> newFileList = new List<string>();
foreach (string str in LbItems) {
DoSomethingWithNewString(str); //these 2 lines are always paired?
lbFiles.Items.Add(str); // this should NOT be needed
}
}
}
Am I missing a binding?
Do you fire PropertyChanged when LbItems is set? It does not look that way. In the constructor, you call InitializeComponent first and then initialize the collection in LbItems = new ObservableCollection<string>();. I think that your collection is initialized "too late", because the binding will already have been processed. If you do not fire a property changed when LbItems is set then the binding will not be updated to actually bind to the collection.
What do I need to add to set a public property on my ViewModel instance from my View? I'd like to set some properties on the ViewModel resource rather than bind it from some element in my view.
View XAML:
<UserControl.Resources>
<vm:MainViewModel x:Key="mainViewModel" MyProperty="30" />
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source={StaticResource mainViewModel}" />
</UserControl.DataContext>
MainViewModel.cs (implements INotifyPropertyChanged)
private int _myProperty;
public int MyProperty{
get { return _myProperty; }
set
{
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}
The setter on MyProperty is never called. There must be some fundamental MVVM thing i'm doing wrong.
Normally you would create a binding which binds the property on the ViewModel with a property of a control. For example you could bind MyProperty to a textbox like so:
<TextBox Text="{Binding MyProperty}" />
Since the parent data context specified by UserControl.DataContext is an instance of MainViewModel, this binding will bind to a property of that object.
Well what you can do is set the MouseDown of a control such as a 'save' button on a method of the code-behind of your view. Then in the codebehind, you set your ViewModel's property or call his method.
In your View.xaml.cs you need something like this
private MyViewModele myVM;
public MyView()
{
InitializeComponent();
Loaded += new RoutedEventHandler(Initialized); //After loading, call Initialized(...)
}
private void Initialized(object sender, RoutedEventArgs e)
{
myVM= this.DataContext as MyViewModele ; //Reference to your ViewModel
}
private void Label_General(object sender, RoutedEventArgs e)
{
myVM.Property = "w/e"; //Set the ViewModel property
}
In your View.xaml
<Label
Content="Click this label"
MouseDown="Label_General"
>
</Label>
Here i setted the Property to a static string but you can retrive any of your View's control and use its value to push it in your ViewModel.
I hope this answer your question.
My psuedo code above actually works. I had another issue with my ViewModel's constructor which had me stumped.
I would like to bind data of my listbox. Imagine I have something like :
<ListBox ItemsSource="{Binding MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text={Binding Value} />
<TextBlock Text={Binding AbsoluteValue} />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MyList contains an observable collection of an object that has a property named "Value"
AbsoluteValue is a property of the view model, as the MyList property.
Of course, the seconde textbox will have always the same value, but it is what I want :)
How can I tell the binding that the datacontext is not the same for the second textbox ?
Thanks in advance for any help
EDIT : my real sample is a StackPanel.
I've tryed
private void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
StackPanel stackPanel = sender as StackPanel;
stackPanel.SetBinding(StackPanel.VisibilityProperty, new Binding("Loaded") { Source = DataContext, Mode = BindingMode.TwoWay });
}
but it's not working
XAML:
<TextBlock x:Name="tbAbsoluteValue" Loaded="AbsoluteValue_Loaded" />
Codebehind:
void AbsoluteValue_Loaded(object sender, RoutedEventArgs e)
{
TextBlock absoluteValue = sender as TextBlock;
absoluteValue.SetBinding(TextBlock.TextProperty, new Binding("AbsoluteValue") { Source = VIEW_MODEL_OBJECT, Mode = BindingMode.TwoWay });
}
That's one way to achieve what you want, you could also use a converter too, or create a StaticResource in your Resources for the VM and bind to that as a source.
I have a listbox with a bunch of contols in each list item.
<ListBox x:Name="projectList" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<ListBox x:Name="taskList" ItemsSource="{Binding Tasks}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="textBoxTask" />
<Button
x:Name="ButtonAddNewTask"
Content="Test"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}"
Click="ButtonAddNewTask_Click"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When I click on the button in the listbox i want to add a new item to the listbox within the listbox. I've come this far. So my question is how do I get hold of the textbox and how do I update the listbox?
Here is my click event
private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
Project proj = button.DataContext as Project;
if(proj.Tasks == null)
proj.Tasks = new List<Task>();
proj.Tasks.Add(new Task("Added Task"));
}
Thanx
The easiest solution would likely be to have one object represent each item in the outer ListBox. It would then have properties that would represent each control in the item - the text in the TextBox, and the items in the ListBox (a list of Tasks, I think, based on your Click handler).
In your Click handler, you can get the Button's DataContext (which should be an item in the collection of the outer list), and add a new Task to that object's list of tasks. Since the inner ListBox is bound to that list, it should be updated with the new item (assuming that it sends events when items are added, such as with ObservableCollection).
Update: Based on your comments, the following should work.
Your Project class should have two properties:
class Project
{
public string Name { get; set; }
private ObservableCollection<Task> tasks =
new ObservableCollection<Task>();
public IList<Task> Tasks
{
get { return this.tasks; }
}
}
The Task class just has one property - the name of the task.
The ProjectView class is a wrapper around the Project class (I got this idea from #timothymcgrath's answer). It keeps track of the name of a new task, and the current Project:
class ProjectView : INotifyPropertyChanged
{
public Project Project { get; set; }
private string newTaskName = string.Empty;
public string NewTaskName
{
get { return this.newTaskName; }
set
{
this.newTaskName = value;
this.OnPropertyChanged("NewTaskName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
PropertyChangedEventHandler eh = this.PropertyChanged;
if(null != eh)
{
eh(this, new PropertyChangedEventArgs(propName));
}
}
}
You'll need a new class that will be used as the DataContext. Something like this:
class Model
{
private ObservableCollection<ProjectView> projects =
new ObservableCollection<ProjectView>();
public IList<ProjectView> Projects
{
get { return this.projects; }
}
}
In the code behind, set the DataContext of the object to an instance of the above class:
public class Window1
{
public Window1()
{
this.InitializeComponent();
this.DataContext = this.model;
}
private Model model = new Model();
}
In the XAML, the bindings should be modified to bind to the above properties:
<ListBox x:Name="projectList" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Projects}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Project.Name}" />
<ListBox x:Name="taskList"
ItemsSource="{Binding Project.Tasks}"
DisplayMemberPath="Name" />
<TextBox x:Name="textBoxTask"
Text="{Binding Path=NewTaskName, UpdateSourceTrigger=PropertyChanged}"/>
<Button x:Name="ButtonAddNewTask" Content="Test"
Click="ButtonAddNewTask_Click" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Finally, in the click handler for the button, create the task. The DataContext of the Button will be the ProjectView for that item.
private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
ProjectView curProject = btn.DataContext as Project;
if(null != curProject)
{
curProject.Project.Tasks.Add(new Task()
{
Name = curProject.NewTaskName
});
}
}
Since all of the controls get their values via binding, you don't need to access the control itself to get the data - just use the data structures that are supplying the controls already.
It would probably be better to move the code that creates the Task into another class (possibly Project), but I just left it in the event handler for ease of typing on my part.
Update 2: Modified the above code to move the NewTaskName property into a separate class that wraps an instance of Project for use with the UI. Does this work better for you?
I'm assuming your Project ListBox is populated with an Collection of Project objects. I would add an AddNewTask ICommand to the Project class and expose it through a property. Then bind the Add New Task button to the new AddNewTask ICommand. For the CommandParameter, put the TaskName in and it will be passed into the command.
Try reading up on some MVVM (Model View ViewModel) for some examples of how this works. It is very clean and works great.
This solution worked for the task at hand so to speak.
private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
DependencyObject obj = LogicalTreeHelper.GetParent(button);
StackPanel item = obj as StackPanel;
TextBox textBox = item.FindName("textBoxTask") as TextBox;
ListBox listBox = item.FindName("taskList") as ListBox;
Project proj = button.DataContext as Project;
if(proj.Tasks == null)
proj.Tasks = new List<Task>();
listBox.ItemsSource = proj.Tasks;
listBox.Items.Refresh();
}