I've a ObservableCollection's list that receives data from the database, and i put this data in my grid, by data binding.
So, i've a user control that appears when i click in a item of this grid. I want that a text box of my user control, show the selected item of my grid.
I've tried this using data binding, but the textbox not shows the selected item.. is it possible ?
grid code:
<FlexGrid:C1FlexGrid
x:Name="grid" ItemsSource="{Binding list3, Mode=TwoWay}"
AutoGenerateColumns="False"
HorizontalAlignment="Left" Height="431" Margin="10,147,0,0" VerticalAlignment="Top" Width="1152" SelectionMode="Row" KeepCurrentVisible="True" Tapped="grid_Tapped" >
<FlexGrid:C1FlexGrid.DataContext>
<local:Controller/>
</FlexGrid:C1FlexGrid.DataContext>
<FlexGrid:C1FlexGrid.Columns>
<FlexGrid:Column Binding="{Binding describe}" Header="Describes" Width="800" />
<FlexGrid:Column Binding="{Binding describeNote}" Header="Describes Notes" Width="300" />
</FlexGrid:C1FlexGrid.Columns>
</FlexGrid:C1FlexGrid>
User Control code:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Binding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:FlexGrid="using:C1.Xaml.FlexGrid"
x:Class="Binding.popNotas"
mc:Ignorable="d" Height="281.925" Width="656.03">
<Grid>
<TextBox x:Name="txt2" Text="{Binding SelectedItem.describe, ElementName=grid, Mode=TwoWay}" Height="38" Margin="140,5,141,0" TextWrapping="Wrap" VerticalAlignment="Top">
</TextBox>
</Grid>
cs code
public class Controller : Common.BindableBase
{
//DAOS
public TesteDao dao { get; set; }
private ObservableCollection<ClPasso> _list3 = new ObservableCollection<ClPasso>();
public ObservableCollection<ClPasso> list3
{
get { return _list3; }
set { this.SetProperty(ref this._list3, value); }
}
public Controller()
{
OnNavigatedTo();
}
protected async void OnNavigatedTo()
{
await InitializeDatabase();
list3 = await createlist3();
}
private async Task InitializeDatabase()
{
string datbasePath = Windows.Storage.ApplicationData.Current.LocalFolder.Path + "\\bd_example.db";
DataBase database = new DataBase (datbasePath);
await database.initialize();
dao = new TesteDao(database);
}
public async Task<ObservableCollection<ClPasso>> createlist3()
{
return await dao.joinListAsync(123, "924be4cc-16db-40c2-b342-d6c1fccbec86");
}
}
Help!
Thanks!!!
i've solved my question..
So, i created a DependencyProperty on code behind of my User Control, named selection, and i put this in Text of my text box.. after, when i will using my user control, in my main page, i passed the value in for porperty..
Like this:
User Control
public sealed partial class UCNotes : UserControl
{
public string selection
{
get { return (string)GetValue(selectionProperty); }
set { SetValue(selectionProperty, value); }
}
public static readonly DependencyProperty selecionadoProperty =
DependencyProperty.Register("selection", typeof(string), typeof(UCNotes), new PropertyMetadata(null));
User Control XAML
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:FlexGrid="using:C1.Xaml.FlexGrid"
x:Class="Fiscalizacao.UCNotes"
mc:Ignorable="d"
d:DesignHeight="327.068" Width="799.248">
<Grid Margin="10,0,10,10" Background="#FFB2B2B2" Height="303" VerticalAlignment="Bottom">
<TextBox x:Name="txtSelection" Text="{Binding selection}" Height="38" Margin="153,75,153,0" TextWrapping="Wrap" VerticalAlignment="Top" BorderBrush="Black"/>
Main Page XAML
<FlexGrid:C1FlexGrid
x:Name="questionsGrid"
HorizontalAlignment="Left" Height="417" Margin="31,181,0,0" VerticalAlignment="Top" Width="1306"
AutoGenerateColumns="False" KeepCurrentVisible="True"
SelectionMode="Row" ItemsSource="{Binding list, Mode=TwoWay}">
<FlexGrid:C1FlexGrid.Columns>
<FlexGrid:Column Binding="{Binding describes}" Header="Descrição" Width="900" />
<FlexGrid:Column Binding="{Binding descibesNotes}" Header="Nota" Width="*" />
</FlexGrid:C1FlexGrid.Columns>
</FlexGrid:C1FlexGrid>
<Popup x:Name="popNotes" IsLightDismissEnabled="True" IsOpen="False" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Grid.Column="1" Margin="0,0,700,300" >
<local:UCNotes selection="{Binding SelectedItem.describes, ElementName=questionsGrid" />
</Popup>
Related
I am still new to WPF and MVVM and am trying to keep the seperation between View and View Model.
i have an app, essentially a projects task list app, in this i create projects and within each project i can create a set of tasks. Most is working well, but essentially i cannot get a command binding on a checkbox in a user control to work using DP, inherited datacontext etc. i always ge a binding failed error when running the app. i am trying to bing to a command in the viewmodel of the view which contains the user controls.
i created a user control to pull the task data together in the view, the command is on the checkbox
<UserControl x:Class="TaskProjectApp.Controls.TaskControl"
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:TaskProjectApp.Controls"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="300">
<Grid Background="LightBlue">
<StackPanel Margin="5,5,5,5">
<TextBlock x:Name="titleTB"
Text="title"
FontSize="20"
FontWeight="Bold"/>
<TextBlock x:Name="DescriptionTB"
Text="description.."
FontSize="15"
Foreground="DodgerBlue"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="priority"
Text="0"
FontSize="15"
FontStyle="Italic"/>
<CheckBox Grid.Column="1"
x:Name="iscomplete"
Command="{Binding SetComplete}"/>
</Grid>
</StackPanel>
</Grid>
</UserControl>
in the user control code behind i have set the DP and the set text function is working
namespace TaskProjectApp.Controls
{
/// <summary>
/// Interaction logic for TaskControl.xaml
/// </summary>
public partial class TaskControl : UserControl
{
public UserTask Task
{
get { return (UserTask)GetValue(TaskProperty); }
set { SetValue(TaskProperty, value); }
}
// Using a DependencyProperty as the backing store for Task. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TaskProperty =
DependencyProperty.Register("Task", typeof(UserTask), typeof(TaskControl), new PropertyMetadata(new UserTask()
{
Title = "title",
Description = "none",
Comments = "none"
}, SetText));
private static void SetText(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TaskControl task = d as TaskControl;
if (task != null)
{
task.titleTB.Text = (e.NewValue as UserTask).Title;
task.DescriptionTB.Text = (e.NewValue as UserTask).Description;
task.priority.Text = (e.NewValue as UserTask).Priority.ToString();
task.iscomplete.IsChecked = (e.NewValue as UserTask).IsComplete;
}
}
public TaskControl()
{
InitializeComponent();
}
}
}
now to make this work i set the binding of the user control in the window as so, the listview takes the usercontrols and implements the observable collection of tasks.
<Window x:Class="TaskProjectApp.Views.ProjectsView"
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:TaskProjectApp.Views"
xmlns:uc="clr-namespace:TaskProjectApp.Controls"
mc:Ignorable="d"
Title="ProjectsView" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<uc:ProjectControl Project="{Binding UserProject}" />
<StackPanel Grid.Row="1">
<TextBlock Text="Task List"/>
<ListView ItemsSource="{Binding Tasks}"
SelectedItem="{Binding SelectedTask}">
<ListView.ItemTemplate>
<DataTemplate>
<uc:TaskControl Task="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Add Task"
Command="{Binding NewProjectTask}"/>
<Button Content="Delete Task"
Command="{Binding DeleteProjectTask}"/>
</StackPanel>
</Grid>
</Window>
this seems to completely stop me using the command, i set the datacontext in the code behind, to the whole window
public partial class ProjectsView : Window
{
public ProjectViewModel ProjectViewModel { get; set; }
public ProjectsView()
{
InitializeComponent();
}
public ProjectsView(UserProject userProject)
{
InitializeComponent();
ProjectViewModel = new ProjectViewModel(userProject);
DataContext = ProjectViewModel;
}
}
and reading trying to solve this has shown that the usercontrol should inherit the datacontext of the parent window.
i have seen solutions using relative paths and DPs for the commands as well as people saying these are not needed just let the inherited datacontext handle it.
but i have tried all three an neither works.
the interface shows me a message box saying no datacontext found, although i notice this is the case when you set the datacontext in code behind and not the xaml.
the SetCommand is created in the projects view model and its a property not a field as i have seen this fail for that reason too.
namespace TaskProjectApp.ViewModels
{
public class ProjectViewModel
{
public UserProject UserProject { get; set; }
public ProjectViewModel(UserProject userProject)
{
UserProject = userProject;
Tasks = new ObservableCollection<UserTask>();
NewProjectTask = new NewProjectTaskCommand(this);
DeleteProjectTask = new DeleteProjectTaskCommand(this);
SetComplete = new SetCompleteCommand();
ReadTaskDatabase();
}
public ObservableCollection<UserTask> Tasks { get; set; }
public NewProjectTaskCommand NewProjectTask { get; set; }
public DeleteProjectTaskCommand DeleteProjectTask { get; set; }
public SetCompleteCommand SetComplete { get; set; }
public UserTask SelectedTask { get; set; }
public void ReadTaskDatabase()
{
List<UserTask> list = new List<UserTask>();
using (SQLiteConnection newConnection = new SQLiteConnection(App.databasePath))
{
newConnection.CreateTable<UserTask>();
list = newConnection.Table<UserTask>().ToList().OrderBy(c => c.Title).ToList();
}
Tasks.Clear();
foreach (UserTask ut in list)
{
if (ut.ProjectId == UserProject.Id)
{
Tasks.Add(ut);
}
}
}
}
}
if anyone can point out where i am going wrong tat will be great as i fear i am now not seeing the wood for the trees.
I found the solution thanks to Ash link Binding to Window.DataContext.ViewModelCommand inside a ItemsControl not sure how i missed it, maybe wrong key words. anyway because the datacontext of the usercontrol is being made into my data class in the observable list Tasks
<StackPanel Grid.Row="1">
<TextBlock Text="Task List"/>
<ListView ItemsSource="{Binding Tasks}"
SelectedItem="{Binding SelectedTask}">
<ListView.ItemTemplate>
<DataTemplate>
<uc:TaskControl Task="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Add Task"
Command="{Binding NewProjectTask}"/>
<Button Content="Delete Task"
Command="{Binding DeleteProjectTask}"/>
</StackPanel>
you need to use a relative path inside the user control to look up past the ItemTemplate to the ListView itself as this uses the viewmodel data context to bind to, so has access to the right level
<UserControl x:Class="TaskProjectApp.Controls.TaskControl"
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:TaskProjectApp.Controls"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="300">
<Grid Background="LightBlue">
<StackPanel Margin="5,5,5,5">
<TextBlock x:Name="titleTB"
Text="title"
FontSize="20"
FontWeight="Bold"/>
<TextBlock x:Name="DescriptionTB"
Text="description.."
FontSize="15"
Foreground="DodgerBlue"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="priority"
Text="0"
FontSize="15"
FontStyle="Italic"/>
<CheckBox Grid.Column="1"
x:Name="iscomplete"
Command="{Binding DataContext.SetComplete, RelativeSource={RelativeSource AncestorType=ListView}}"/>
</Grid>
</StackPanel>
</Grid>
</UserControl>
this might be limiting in future as it measn the usercontrol will look for a listview to bind the command, but it solves the immediate issue.
I am working on my first project with WPF and as practice, have created an application that has a menu bar with two buttons on the side which are used to change the display two different views on the grid to its right. I am using the MVVM pattern. Using the ICommand interface, I am able to change the view in the grid using usercontrols for the two views from the menu bar buttons. The application is as follows:
The menu bar has buttons named View 1 and View 2, clicking any of them will open up the relevant view in the usercontrol. At startup a "defaultview" is displayed. The default view has a button which can also be used to move to View 1. Below is what I am stuck at:
I have a button in View 1 to change the display to View 2 (it calls the same command I made in the App View Model but even though it changes the variable value to which the sidegrid is bound too, the view displayed doesnt change).
I want to pass data (tried with First Name) from View 1 to View 2 but I cant get binding to work.
Can someone guide me or link me to examples or material that discuss this?
App View Model
using DataBindingAndViewsTestProject.Views;
using Hotel_Management_Project_WPF.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataBindingAndViewsTestProject.ViewModels
{
public class AppVM : ObservableObject
{
//Create a property that controls current view
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set { OnPropertyChanged(ref _currentView, value);
}
}
//Instantiate the relaycommands, we will need to instantiate relaycommand objects for every command we need to perform.
//This means that we will need to do this for preses of all buttons
public RelayCommand View1ButtonCommand { get; private set; }
public RelayCommand View2ButtonCommand { get; private set; }
public AppVM()
{
CurrentView = new DefaultVM();
View1ButtonCommand = new RelayCommand(ShowView1, AlwaysTrueCommand);
View2ButtonCommand = new RelayCommand(ShowView2, AlwaysTrueCommand);
}
public void ShowView1(object dummy)
{
CurrentView = new View1();
}
public void ShowView2(object dummy)
{
CurrentView = new View2();
}
public bool AlwaysTrueCommand(object dummy)
{
return true;
}
}
}
Main Window.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="600"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button x:Name="view1Button" Content="Go to View 1" Margin="10" Command="{Binding View1ButtonCommand}"></Button>
<Button x:Name="view2Button" Content="Go to View 2" Margin="10" Command="{Binding View2ButtonCommand}"></Button>
</StackPanel>
<Grid Grid.Column="1">
<ContentControl Content="{Binding CurrentView}"></ContentControl>
</Grid>
</Grid>
</Window>
View1.xaml
<UserControl x:Class="DataBindingAndViewsTestProject.Views.View1"
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:DataBindingAndViewsTestProject.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
xmlns:vm="clr-namespace:DataBindingAndViewsTestProject.ViewModels">
<UserControl.Resources>
<vm:AppVM x:Name="AppVMinView1" x:Key="AppVMinView1"></vm:AppVM>
</UserControl.Resources>
<UserControl.DataContext>
<vm:View1VM></vm:View1VM>
</UserControl.DataContext>
<Grid Background="Aqua">
<StackPanel Margin="100">
<TextBlock Text="First Name"/>
<TextBox x:Name="firstNameTextBoxView1" Text="{Binding View1InfoClass.FirstName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Last Name"/>
<TextBox x:Name="lastNameTextBoxView1" Text="{Binding View1InfoClass.LastName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Random Useless Number" ></TextBlock>
<TextBox x:Name="randomUselessNumberView1" Text="{Binding View1InfoClass.Number, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="First Name Entered"></TextBlock>
<TextBlock Text="{Binding View1InfoClass.FirstName}"></TextBlock>
<TextBlock Text="Last Name Entered" ></TextBlock>
<TextBlock Text="{Binding View1InfoClass.LastName}"></TextBlock>
<TextBlock Text="Random Useless Number Entered"></TextBlock>
<TextBlock Text="{Binding View1InfoClass.Number}"></TextBlock>
<Button DataContext="{StaticResource AppVMinView1}" Content="Go to view2" Height="20" Width="70" Command="{Binding View2ButtonCommand}" />
</StackPanel>
</Grid>
</UserControl>
View2.xaml
<UserControl x:Class="DataBindingAndViewsTestProject.Views.View2"
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:DataBindingAndViewsTestProject.Views"
xmlns:vm="clr-namespace:DataBindingAndViewsTestProject.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<vm:View1VM x:Key="View1VMInView2" x:Name="View1VMInView2"></vm:View1VM>
</UserControl.Resources>
<UserControl.DataContext>
<vm:View2VM></vm:View2VM>
</UserControl.DataContext>
<Grid Background="Beige">
<StackPanel Margin="100">
<TextBlock Text="First Name"/>
<TextBox x:Name="firstNameTextBoxView2" Text="{Binding View2InfoClass.FirstName, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Last Name"/>
<TextBox x:Name="lastNameTextBoxView2" Text="{Binding View2InfoClass.LastName, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Random Useless Number"></TextBlock>
<TextBox x:Name="randomUselessNumberView2" Text="{Binding View2InfoClass.Number, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="First Name Entered"></TextBlock>
<TextBlock DataContext="View1InView2" Text="{Binding View1InfoClass.FirstName}" ></TextBlock>
<TextBlock Text="Last Name Entered"></TextBlock>
<TextBlock></TextBlock>
<TextBlock Text="Random Useless Number Entered"></TextBlock>
<TextBlock ></TextBlock>
</StackPanel>
</Grid>
</UserControl>
Model (if relevant)
public class InfoClass:ObservableObject
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set {
OnPropertyChanged(ref _firstName, value);
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { OnPropertyChanged(ref _lastName, value); }
}
private int _number;
public int Number
{
get { return _number; }
set { OnPropertyChanged(ref _number, value); }
}
}
I'm trying to learn MVVM pattern using WPF C#. And I'm running into an error when trying to close an opened window after saving information to an sqlite database. When the command to save a new contact is raised, I am getting an error on HasAddedContact(this, new EventArgs());
Error: System.NullReferenceException: 'Object reference not set to an instance of an object.'
My ViewModel:
public class NewContactViewModel : BaseViewModel
{
private ContactViewModel _contact;
public ContactViewModel Contact
{
get { return _contact; }
set { SetValue(ref _contact, value); }
}
public SaveNewContactCommand SaveNewContactCommand { get; set; }
public event EventHandler HasAddedContact;
public NewContactViewModel()
{
SaveNewContactCommand = new SaveNewContactCommand(this);
_contact = new ContactViewModel();
}
public void SaveNewContact()
{
var newContact = new Contact()
{
Name = Contact.Name,
Email = Contact.Email,
Phone = Contact.Phone
};
DatabaseConnection.Insert(newContact);
HasAddedContact(this, new EventArgs());
}
}
SaveNewContactCommand:
public class SaveNewContactCommand : ICommand
{
public NewContactViewModel VM { get; set; }
public SaveNewContactCommand(NewContactViewModel vm)
{
VM = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
VM.SaveNewContact();
}
}
NewContactWindow.Xaml.Cs code behind:
public partial class NewContactWindow : Window
{
NewContactViewModel _viewModel;
public NewContactWindow()
{
InitializeComponent();
_viewModel = new NewContactViewModel();
DataContext = _viewModel;
_viewModel.HasAddedContact += Vm_ContactAdded;
}
private void Vm_ContactAdded(object sender, EventArgs e)
{
this.Close();
}
}
Adding additional code where I call the new window:
public class ContactsViewModel
{
public ObservableCollection<IContact> Contacts { get; set; } = new ObservableCollection<IContact>();
public NewContactCommand NewContactCommand { get; set; }
public ContactsViewModel()
{
NewContactCommand = new NewContactCommand(this);
GetContacts();
}
public void GetContacts()
{
using(var conn = new SQLite.SQLiteConnection(DatabaseConnection.dbFile))
{
conn.CreateTable<Contact>();
var contacts = conn.Table<Contact>().ToList();
Contacts.Clear();
foreach (var contact in contacts)
{
Contacts.Add(contact);
}
}
}
public void CreateNewContact()
{
var newContactWindow = new NewContactWindow();
newContactWindow.ShowDialog();
GetContacts();
}
}
ContactsWindow.Xaml
<Window x:Class="Contacts_App.View.ContactsWindow"
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:Contacts_App.View"
xmlns:vm="clr-namespace:Contacts_App.ViewModel"
mc:Ignorable="d"
Title="Contacts Window" Height="320" Width="400">
<Window.Resources>
<vm:ContactsViewModel x:Key="vm"/>
</Window.Resources>
<StackPanel Margin="10">
<Button
Content="New Contact"
Command="{Binding NewContactCommand}"/>
<TextBox Margin="0,5,0,5"/>
<ListView
Height="200"
Margin="0,5,0,0"
ItemsSource="{Binding Contacts}">
<ListView.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Window>
NewContactWindow.Xaml
<Window x:Class="Contacts_App.View.NewContactWindow"
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:Contacts_App.View"
xmlns:vm="clr-namespace:Contacts_App.ViewModel"
mc:Ignorable="d"
Title="New Contact Window" Height="250" Width="350">
<Window.Resources>
<vm:NewContactViewModel x:Key="vm"/>
</Window.Resources>
<Grid>
<StackPanel
Margin="10">
<Label Content="Name" />
<TextBox
Text="{Binding Source={StaticResource vm}, Path=Contact.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Label Content="Email" />
<TextBox
Text="{Binding Source={StaticResource vm}, Path=Contact.Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Label Content="Phone Number" />
<TextBox
Text="{Binding Source={StaticResource vm}, Path=Contact.Phone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Button
Content="Save"
Command="{Binding Source={StaticResource vm}, Path=SaveNewContactCommand}"/>
</StackPanel>
</Grid>
</Window>
You're creating NewContactWindow's viewmodel in the constructor, correctly assigning it to DataContext, and correctly adding a handler to that event. Unfortunately, you also create a second instance of the same viewmodel in resources, and you manually set the Source property of all the bindings to use the one in the resources, which doesn't have the event handler.
Window.DataContext, which you set in the constructor, is the default Source for any binding in the Window XAML. Just let it do its thing. I also removed all the redundant Mode=TwoWay things from the Bindings to TextBox.Text, since that property is defined so that all bindings on it will be TwoWay by default. I don't think UpdateSourceTrigger=PropertyChanged is doing anything necessary or helpful either: That causes the Binding to update your viewmodel property every time a key is pressed, instead of just when the TextBox loses focus. But I don't think you're doing anything with the properties where that would matter; there's no validation or anything. But TextBox.Text is one of the very few places where that's actually used, so I left it in.
You should remove the analagous viewmodel resource in your other window. It's not doing any harm, but it's useless at best. At worst, it's an attractive nuisance. Kill it with fire and bury the ashes under a lonely crossroads at midnight.
<Window x:Class="Contacts_App.View.NewContactWindow"
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:Contacts_App.View"
xmlns:vm="clr-namespace:Contacts_App.ViewModel"
mc:Ignorable="d"
Title="New Contact Window" Height="250" Width="350">
<Grid>
<StackPanel
Margin="10">
<Label Content="Name" />
<TextBox
Text="{Binding Contact.Name, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Label Content="Email" />
<TextBox
Text="{Binding Contact.Email, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Label Content="Phone Number" />
<TextBox
Text="{Binding Contact.Phone, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Button
Content="Save"
Command="{Binding SaveNewContactCommand}"/>
</StackPanel>
</Grid>
</Window>
I am currently working within a WPF user control using MVVM. My MainWindow.xaml looks like below.
MainWindow.xaml
<Window.Resources>
<ObjectDataProvider x:Key="TabsList" ObjectType="{x:Type local:MainWindowModel}" MethodName="GetTabs"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding Source={StaticResource TabsList}}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=TabName}" Margin="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ContentControl Grid.Column="1" Content="{Binding Source={StaticResource TabsList}, Path=MyUserControl}"/>
</Grid>
Data provider class is as below
public class MainWindowModel
{
public List<TabInfo> GetTabs()
{
return new List<TabInfo>()
{
new TabInfo() { TabName="Tab1", MyUserControl = new UserControl1()},
new TabInfo() { TabName="Tab2", MyUserControl = new UserControl2()}
};
}
}
public class TabInfo
{
public string TabName { get; set; }
public UserControl MyUserControl { get; set; }
}
And now I have two usercontrols UserControl1 and UserControl2 each has a text box control. I would like to update the Text property of the textbox control in the UserControl1 whenever the Text property of the Textbox control in the UserControl2 is updated. To do this, I tried as below.
UserControl1
<UserControl x:Class="MoreOnBinding2.UserControl1"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox Width="200" Height="20" Text="{Binding Path=UserControl1Text}"/>
</Grid>
UserControl1ViewModel
public class UserControl1VM : ViewModelBase
{
public UserControl1VM()
{
this.UserControl1Text = "10";
}
private string userControl1Text;
public string UserControl1Text
{
get { return userControl1Text; }
set
{
if (userControl1Text != value)
{
userControl1Text = value;
RaisePropertyChanged(() => UserControl1Text);
}
}
}
}
UserControl2
<UserControl x:Class="MoreOnBinding2.UserControl2"
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:MoreOnBinding2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox Width="200" Height="20"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UserControl1}},
UpdateSourceTrigger=PropertyChanged, Path=UserControl1Text}" />
</Grid>
But it is not working. It seems there is a problem in the RelativeSource tag in the UserControl2. Can someone help on this.
You could use a Dependency Property on your user controls this would give you a bind able property. see link
EDIT 1
Just had another look at your class setup. If you are following the MVVM approach I don't think you should have a reference to your user control in your view model
View Model should be more Like
public Class
{
ObservableCollection<TabInfo> _Tabs = new ObservableCollection<TabInfo>();
public ObservableCollection<TabInfo> Tabs
{
get{return _Tabs;}
set {_Tabs = value;}//Need to implement INotifyPropertyChanged [Link][2]
}
public TabInfo SelectedTab {get;set;}
}
public class TabInfo
{
public string TabName { get; set; }
public UserControlViewModel MyUserControlViewModel{ get; set; }
}
Then in your View
<Window.DataContext>
<vm:MainWindowModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding Tabs}" IsSynchronizedWithCurrentItem="True" SelectedValue="{Binding SelectedTab}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=TabName}" Margin="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ContentControl Grid.Column="1" Content="{Binding SelectedTab.MyUserControlViewModel"/>
</Grid>
This should give you a list of Tabs and then when one is selected it will set the content of the ContentControl to the MyUserControlViewModel.
You would then need to use some sort of template selector to control the ContentTemplate to load different UserControls into the ContentControl
I haven't tested the code works and you would need to implement the INotifyPropertyChanged on all the public properties so the bindings update as the properties value is changed.
Hope that helps a bit.
I can't seem to use the ItemSource Tag for my treeview. I don't unerstand the problem..
I am trying a simple tree view before i actually bind to my database.. I a looking for an MVVM Style solution
Here is My view Model
public class TreeViewVM : ViewModelBase
{
public class Topic
{
public string Title { get; set; }
public int Rating { get; set; }
private ObservableCollection<Topic> childTopicsValue = new ObservableCollection<Topic>();
public ObservableCollection<Topic> ChildTopics {
get
{
return childTopicsValue;
}
set
{
childTopicsValue = value;
}
}
public Topic() {}
public Topic(string title, int rating)
{
Title = title;
Rating = rating;
}
}
static public ObservableCollection<Topic> Users = new ObservableCollection<Topic>();
public TreeViewVM()
{
Users.Add(new Topic("Using Controls and Dialog Boxes", -1));
Users.Add(new Topic("Getting Started with Controls", 1));
Topic DataGridTopic = new Topic("DataGrid", 4);
DataGridTopic.ChildTopics.Add(
new Topic("Default Keyboard and Mouse Behavior in the DataGrid Control", -1));
DataGridTopic.ChildTopics.Add(
new Topic("How to: Add a DataGrid Control to a Page", -1));
DataGridTopic.ChildTopics.Add(
new Topic("How to: Display and Configure Row Details in the DataGrid Control", 1));
Users.Add(DataGridTopic);
Topics = Users;
}
private ObservableCollection<Topic> _Topics { get; set; }
public ObservableCollection<Topic> Topics
{
get
{
return _Topics;
}
set
{
if (_Topics != value)
{
_Topics = value;
OnNotifyPropertyChanged("Topics");
}
}
}
}
}
Here is my Xaml
xmlns:converter="clr-namespace:TestTree"
xmlns:viewModel="clr-namespace:TestTree.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<UserControl.Resources>
<viewModel:TreeViewVM x:Key="ViewModel" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource Topics}}">
<StackPanel x:Name="LayoutRoot2" Background="White">
<StackPanel.Resources>
<sdk:HierarchicalDataTemplate x:Key="ChildTemplate" >
<TextBlock FontStyle="Italic" Text="{Binding Path=Title}" />
</sdk:HierarchicalDataTemplate>
<sdk:HierarchicalDataTemplate x:Key="NameTemplate"
ItemsSource="{Binding Path=ChildTopics}"
ItemTemplate="{StaticResource ChildTemplate}">
<TextBlock Text="{Binding Path=Title}" FontWeight="Bold" />
</sdk:HierarchicalDataTemplate>
</StackPanel.Resources>
<sdk:TreeView Width="400" Height="300" DataContext="{Binding Path=Topics}" ItemTemplate="{StaticResource NameTemplate}" x:Name="myTreeView" />
</StackPanel>
First of all you set DataContext of the "LayourRoot" grid to a resource with key "Topics" which does not exist. That probably should be the "ViewModel" resource. Second of all, why can't you use the ItemsSource property on the TreeView? Setting DataContext property on the TreeView alone will not work. Here is the correct XAML:
<UserControl x:Class="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">
<UserControl.Resources>
<viewModel:TreeViewVM x:Key="ViewModel" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource ViewModel}}">
<StackPanel x:Name="LayoutRoot2" Background="White">
<StackPanel.Resources>
<sdk:HierarchicalDataTemplate x:Key="ChildTemplate" >
<TextBlock FontStyle="Italic" Text="{Binding Path=Title}" />
</sdk:HierarchicalDataTemplate>
<sdk:HierarchicalDataTemplate x:Key="NameTemplate"
ItemsSource="{Binding Path=ChildTopics}"
ItemTemplate="{StaticResource ChildTemplate}">
<TextBlock Text="{Binding Path=Title}" FontWeight="Bold" />
</sdk:HierarchicalDataTemplate>
</StackPanel.Resources>
<sdk:TreeView Width="400" Height="300" ItemsSource="{Binding Path=Topics}" ItemTemplate="{StaticResource NameTemplate}" x:Name="myTreeView" />
</StackPanel>
</Grid>
</UserControl>