I have been all over the internet looking for a clear answer and cannot find one. I have created Window Forms apps in the past, and decided to give WPF a try. I am hoping I don't have to move back to WinForms, but cannot figure out the simplest tasks.
How do I pass text in Page1's Textbox to Page2's TextBox.
I apologize if this is a duplicate, but I cannot find a clear answer anywhere. Most beginner mvvm tutorials seem to stick to one page apps.
MainWindo.xaml
<Window x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Frame Source="/Page1.xaml"/>
</Grid>
</Window>
Page1 Xaml:
<Page x:Class="WpfApp3.Page1"
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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1"
xmlns:vm="clr-namespace:WpfApp3">
<Page.DataContext>
<vm:Page1ViewModel/>
</Page.DataContext>
<Grid>
<StackPanel>
<TextBlock Margin="20" FontSize="36">Welcome Home</TextBlock>
<TextBox x:Name="UserName" Margin="10 0 10 0" Text=""/>
<Button x:Name="Next" Margin="10 10" Content="Next" Click="Next_Click"/>
</StackPanel>
</Grid>
</Page>
Page1 Code-Behind:
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp3
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
}
private void Next_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(
new Uri("/Page2.xaml", UriKind.Relative));
}
}
}
Page1 ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApp3
{
class Page1ViewModel : Notifier
{
private string username;
public string UserName
{
get { return username; }
set
{
username = value;
OnPropertyChanged("UserName");
}
}
}
}
Page2 Xaml:
<Page x:Class="WpfApp3.Page2"
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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page2"
xmlns:vm="clr-namespace:WpfApp3>
<Page.DataContext>
<vm:Page2ViewModel/>
<Page.Data.Context>
<Grid>
<StackPanel>
<TextBlock Margin="10">Hello there: </TextBlock>
<TextBox x:Name="TextBox_Name" Margin="10" Text="{Binding UserName}"/>
</StackPanel>
</Grid>
</Page>
Page2 Code-behind:
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp3
{
/// <summary>
/// Interaction logic for Page2.xaml
/// </summary>
public partial class Page2 : Page
{
public Page2()
{
InitializeComponent();
}
}
}
Page2 View Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApp3
{
class Page2ViewModel : Notifier
{
private string textbox_name;
public string TextBox_Name
{
get { return textbox_name; }
set
{
textbox_name = value;
OnPropertyChanged("TextBox_Name");
}
}
}
}
first of all you don't need to move back to WinForms: you can avoid MVVM alltogether and use the traditional code-behind coding model you know even with WPF. I'd suggest doing so for your first WPF applications if you don't feel at ease with MVVM.
As for your problem, a solution could be to use static properties for common values storage.
public class MyCommonValues
{
public static string SharedText { get; set; }
}
class Page1ViewModel : Notifier
{
private string username;
public string UserName
{
get { return username; }
set
{
username = value;
OnPropertyChanged("UserName");
OnUserNameChanged(); // added to your code
}
}
// following is an addition to your code
void OnUserNameChanged()
{
MyCommonValues.SharedText = UserName;
}
}
class Page2ViewModel : Notifier
{
// following is an addition to your code
public Page2ViewModel()
{
TextBox_Name = MyCommonValues.SharedText;
}
private string textbox_name;
public string TextBox_Name
{
get { return textbox_name; }
set
{
textbox_name = value;
OnPropertyChanged("TextBox_Name");
}
}
}
Related
I know this question has been posted already, but I don't understand the answers. In My case I have a project with only a UserControl:
<UserControl x:Class="SimpleOxyPlotUserControl.UserControl1View"
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:SimpleOxyPlotUserControl"
xmlns:oxy="http://oxyplot.org/wpf"
mc:Ignorable="d"
x:Name="uc1"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<oxy:PlotView Model="{Binding ElementName=uc1, Path=OxyPlotModel, Mode=TwoWay}"/>
</Grid>
</UserControl>
and its code behind:
using System.Windows;
using System.Windows.Controls;
using OxyPlot;
namespace SimpleOxyPlotUserControl
{
/// <summary>
/// Interaction logic for UserControl1View.xaml
/// </summary>
public partial class UserControl1View : UserControl
{
public UserControl1View() => InitializeComponent();
public PlotModel OxyPlotModel
{
get { return (PlotModel)GetValue(OxyPlotModelProperty); }
set { SetValue(OxyPlotModelProperty, value); }
}
public static readonly DependencyProperty OxyPlotModelProperty =
DependencyProperty.Register("OxyPlotModel", typeof(PlotModel), typeof(UserControl1View),
new PropertyMetadata(new PlotModel()));
}
}
Then I have another project with a simple WPF App and its MainWindow.xaml:
<Window x:Class="SimpleOxyPlotUserControlApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:simpleOxyPlotUserControl="clr-namespace:SimpleOxyPlotUserControl;assembly=SimpleOxyPlotUserControl"
Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<simpleOxyPlotUserControl:UserControl1View/>
<simpleOxyPlotUserControl:UserControl1View Grid.Row="1"/>
</Grid>
</Window>
and its code behind:
using System.Windows;
namespace SimpleOxyPlotUserControlApp.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
I receive the error message "InvalidOperationException: This PlotModel is already in use by some other PlotView control."
I know this is, because each Oxy PlotModel can only be connected to exactly one Oxy PlotView. But why does it not create a new Oxy PlotView and PlotModel each time, when I insert my UserControl? And how can I ensure that it does so?
OxyPlotModelProperty is registered with new PlotModel() default value in PropertyMetadata. but OxyPlotModelProperty is static so you get the same PlotModel instance for all UserControl1View instances.
fix it by creating PlotModel in constructor:
public partial class UserControl1View : UserControl
{
public UserControl1View()
{
InitializeComponent();
SetLocalValue(OxyPlotModelProperty, new PlotModel());
}
public PlotModel OxyPlotModel
{
get { return (PlotModel)GetValue(OxyPlotModelProperty); }
set { SetValue(OxyPlotModelProperty, value); }
}
public static readonly DependencyProperty OxyPlotModelProperty =
DependencyProperty.Register("OxyPlotModel", typeof(PlotModel), typeof(UserControl1View),
new PropertyMetadata(null));
}
I am writing a User Control named "GridAndChart" that can be filled with values at Design-Time just like this:
<local:GridAndChart HorizontalAlignment="Left"
VerticalAlignment="Top" Width="660"
Height="342"
Margin="28,29,0,0"
NameX="Auslastung in %"
NameY="Wirkungsgrad in %"
MinValueX="0"
MaxValueX="100"
MinValueY="0"
MaxValueY="100"
TitleOfChart="Kennlinie Wirkungsgrad"
Xvals='12,323,43,55,65,7'
Yvals='60,99,99,99,99,99'
/>
Most of the Properties you can see there (Like TitleOfChart, Xvals,...) I defined in the Code-Behind-File of my User-Control as Dependency-Properties.
My Problem is that only those Dependency-Properties are displayed correctly in my Test-Window, that are bound in the UserControl-XAML and therefore invoked by the initializeComponent(), like for example TitleOfChart. By displayed correctly I mean: Display of the Design-time-value from the XAML where I include my UserControl. The other dependency properties don't display correctly because they show only their default value.
From my example you will see that the Evaluation of my dummy-calculations will happen with the default values of the Dependency Properties. Not, after that they got their "Design-Time-Values", read out from the Main.xaml.
Thankyou for your help!
EDIT: Reproducible Code (4 Files)
File 1 and 2: The Window that uses the Control and it's Code-Behind
MainWindow.xaml
<Window x:Class="MinimalReproducibleExample.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:MinimalReproducibleExample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:SimpleUserControl Working="I defined this Text at Design time" NotWorking="1,2,3,4"/>
</Grid>
</Window>
MainWindow.xaml.cs (I left it empty)
using ...
namespace MinimalReproducibleExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
File 3 and 4: The User-Control and it's Code-Behind
SimpleUserControl.xaml
<UserControl x:Class="MinimalReproducibleExample.SimpleUserControl"
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:MinimalReproducibleExample"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Name="TextBox1XAML" />
<TextBox Grid.Row="1" Name="TextBox2XAML" />
</Grid>
</UserControl>
SimpleUserControl.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MinimalReproducibleExample
{
public partial class SimpleUserControl : UserControl
{
public SimpleUserControl()
{
InitializeComponent();
string notWorking = NotWorking;//just to show that there happens some calculation with the values
int a = Convert.ToInt32(notWorking.ElementAt(0)); //before they are displayed
int b = Convert.ToInt32(notWorking.ElementAt(1));
int c = a + b;
TextBox2XAML.Text = c.ToString();
}
public string Working
{
get { return GetValue(WorkingProperty).ToString(); }
set { SetValue(WorkingProperty, value); }
}
public static readonly DependencyProperty WorkingProperty = DependencyProperty.Register(nameof(Working), typeof(string), typeof(SimpleUserControl), new PropertyMetadata("The default is working.", (s, e) => (s as SimpleUserControl).TextBox1XAML.Text = (string)e.NewValue));
public string NotWorking
{
get { return GetValue(NotWorkingProperty).ToString(); }
set { SetValue(NotWorkingProperty, value); }
}
public static readonly DependencyProperty NotWorkingProperty = DependencyProperty.Register(nameof(NotWorking), typeof(string), typeof(SimpleUserControl), new PropertyMetadata("The default is also working.", (s, e) => (s as SimpleUserControl).NotWorking = (string)e.NewValue));
}
}
In your MCVE if you put the code to the Loaded event handler of your usercontrol, then all will work as expected. See example:
Usercontrol:
public partial class SimpleUserControl : UserControl
{
public string SimpleTxtProp { get; set; } = "AAA";
public SimpleUserControl()
{
//Constructor being called from InitializeComponent() of parent element. All properties are initialized with values from code behind,
//after object creation is finished, it's property will be initialized with values set in parent element for this control.
InitializeComponent();//Content of control being initialized: constructor -> InitializeComponent()
var isAAA = SimpleTxtProp == "AAA";//true
Loaded += (o, e) =>
{
var isBBB = SimpleTxtProp == "BBB"; //true
};
}
}
MainWindow:
<local:SimpleUserControl SimpleTxtProp="BBB"/>
It doesn't matter whether you consider dependency or simple property.
InitializeComponent() of MainWindow does
trigger the instantiation of SimpleUserControl, hence constructor
with default values/values from code behind being called,
then the properties of the created object being set.
So once an istance of "SimpleUserControl" is loaded, it has the values, which was set in XAML of MainWindow.
I am stuck with a C# wpf drag drop issue. I have created a very simple project that includes a User Control, a couple of classes to hold the data and a form to host multiple copies of the user control (using a bound ItemsControl). When I drag the control onto the form the drag drop is triggered, the observablecollection is updated but the UI doesn't reflect the change and future events don't seem to be working. Rolling over the add item button doesn't even show the rollover effect. Sure I am doing something stupid but I can't seem to see what it is.
Code below (mainly from Microsoft Example)
SimpleDataClass
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DragDropControl.Model
{
public class SimpleDataClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _groupName = string.Empty;
private ObservableCollection<SimpleSubDataClass> _titles = new ObservableCollection<SimpleSubDataClass>();
public string GroupName
{
get { return _groupName; }
set
{
if (_groupName != value)
{
_groupName = value;
RaisePropertyChangedEvent("GroupName");
}
}
}
public ObservableCollection<SimpleSubDataClass> Titles
{
get { return _titles; }
set
{
if (_titles != value)
{
_titles = value;
RaisePropertyChangedEvent("Titles");
}
}
}
private void RaisePropertyChangedEvent(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
SimpleSubDataClass
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DragDropControl.Model
{
public class SimpleSubDataClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _title = string.Empty;
public string Title
{
get { return _title; }
set
{
if (_title != value)
{
_title = value;
RaisePropertyChanged("Title");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public SimpleSubDataClass(string title)
{
Title = title;
}
}
}
DDControl - XAML
<UserControl x:Class="DragDropControl.DDControl"
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:DragDropControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="CurrentControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Name="txtGroupName" Grid.Row="0" Text="{Binding ElementName=CurrentControl, Path=ThisData.GroupName}"/>
<ListBox Name="lstTitles" Grid.Row="1" ItemsSource="{Binding ElementName=CurrentControl, Path=ThisData.Titles}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Name="lblTitle" Content="{Binding Title}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
DDControl - Code behind
using DragDropControl.Model;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace DragDropControl
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class DDControl : UserControl
{
public static readonly DependencyProperty ThisDataProperty = DependencyProperty.Register( "ThisData",
typeof(SimpleDataClass),
typeof(DDControl),
new PropertyMetadata(new SimpleDataClass()));
public SimpleDataClass ThisData
{
get { return (SimpleDataClass)GetValue(ThisDataProperty); }
set { SetValue(ThisDataProperty, value); }
}
public DDControl()
{
InitializeComponent();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed)
{
DataObject data = new DataObject(this.ThisData);
DragDrop.DoDragDrop(this, data, DragDropEffects.Move);
}
}
protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
{
base.OnGiveFeedback(e);
if (e.Effects.HasFlag(DragDropEffects.Move))
Mouse.SetCursor(Cursors.Pen);
e.Handled = true;
}
}
}
MainWindow - Xaml
<Window x:Class="DragDropUserControlWithTextBox.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:DragDropUserControlWithTextBox"
xmlns:ddc="clr-namespace:DragDropControl;assembly=DragDropControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Name="stkMain" Background="Gray" Orientation="Horizontal" Drop="stkMain_Drop" AllowDrop="true">
<ItemsControl Name="icColumns" Background="Red">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Name="stkItemsControlPanel" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ddc:DDControl Background="{x:Null}" ThisData="{Binding}"/><!-- MouseMove="DDControl_MouseMove" GiveFeedback="DDControl_GiveFeedback"/>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Name="btnAddData" Content="Add Data" Click="btnAddData_Click"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
MainWindow - Code behind
using DragDropControl.Model;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
namespace DragDropUserControlWithTextBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<SimpleDataClass> _data = new ObservableCollection<SimpleDataClass>();
public MainWindow()
{
InitializeComponent();
CreateTestData();
}
private void CreateTestData()
{
SimpleDataClass tempSDC1 = new SimpleDataClass();
tempSDC1.GroupName = "First Item";
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_1"));
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_2"));
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_3"));
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_4"));
tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_5"));
SimpleDataClass tempSDC2 = new SimpleDataClass();
tempSDC2.GroupName = "Second Item";
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_1"));
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_2"));
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_3"));
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_4"));
tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_5"));
_data.Add(tempSDC1);
_data.Add(tempSDC2);
this.icColumns.ItemsSource = _data;
}
private void stkMain_Drop(object sender, DragEventArgs e)
{
if (e.Handled == false)
{
if (e.Data.GetDataPresent(typeof(SimpleDataClass)))
{
SimpleDataClass tempData = (SimpleDataClass)e.Data.GetData(typeof(SimpleDataClass));
_data.Add(tempData);
}
e.Effects.HasFlag(DragDropEffects.None);
Mouse.SetCursor(Cursors.Arrow);
e.Handled = true;
}
}
private void btnAddData_Click(object sender, RoutedEventArgs e)
{
SimpleDataClass tempData = new SimpleDataClass();
tempData.GroupName = "Amazing Test";
tempData.Titles.Add(new SimpleSubDataClass("AT_1"));
tempData.Titles.Add(new SimpleSubDataClass("AT_2"));
tempData.Titles.Add(new SimpleSubDataClass("AT_3"));
_data.Add(tempData);
}
}
}
I swapped to using the method described in this WPF tutorial on drag and drop. It still had an issue when you dragged and dropped a user control with a textbox on it (would assume it would be for any control that is hitenabled) where it would create a second instance of the item being dragged but that is pretty easy to work around, just set the enabled state to false when detecting the control is about to be dragged and enable it again when it is dropped. Possibly a hack but one that works.
For drag and drop, create a Behavior and handle the events in the Behavior for the avoiding the UI thread freeze scenarios. Check the below link will be useful for your scenario.
https://www.telerik.com/blogs/adding-drag-and-drop-to-wpf-listboxes-thanks-telerik!
I have asked how to bind listbox to mvvm in this thread:
Listbox is not Populating using binding
I created another thread to ask why my ObservableList is not Refreshing.
The List is now populated on Load
but not populating or refreshing after I select the Folder
The program is supposed to Populate the list after I select the program
this is the structure of my folders and class
this is the code for my FolderBrowserDialogVM
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Bates_Writer.ViewModel
{
public class FolderBrowserDialogVM :ViewModelBase
{
public System.Collections.ObjectModel.ObservableCollection<string> FileNames { get; }
= new System.Collections.ObjectModel.ObservableCollection<string>()
{
"Wayne",
"Cinderella",
"Malificient"
};
public static RelayCommand OpenCommand { get; set; }
private string _selectedPath;
public string SelectedPath
{
get { return _selectedPath; }
set
{
_selectedPath = value;
RaisePropertyChanged("SelectedPath");
}
}
private string _defaultPath;
public FolderBrowserDialogVM()
{
RegisterCommands();
}
public FolderBrowserDialogVM(string defaultPath)
{
_defaultPath = defaultPath;
RegisterCommands();
}
private void RegisterCommands()
{
OpenCommand = new RelayCommand(ExecuteOpenFileDialog);
}
private void ExecuteOpenFileDialog()
{
var dialog = new FolderBrowserDialog();
dialog.ShowDialog();
SelectedPath = dialog.SelectedPath;
FileNames.Clear();
foreach (var file in System.IO.Directory.EnumerateFiles(SelectedPath, "*", System.IO.SearchOption.AllDirectories))
{
FileNames.Add(file);
Console.WriteLine(file);
}
}
}
}
this is the code for FilesView.xaml(user control)
<UserControl x:Class="Bates_Writer.View.FilesView"
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:Bates_Writer.View"
xmlns:vm="clr-namespace:Bates_Writer.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<vm:FolderBrowserDialogVM>
</vm:FolderBrowserDialogVM>
</UserControl.DataContext>
<Grid>
<ListBox ItemsSource="{Binding FileNames}" Margin="5,5,5,5"/>
</Grid>
</UserControl>
I tried How to: Implement Property Change Notification
but I can't make it work.
this is my FolderBrowserDialogV.xaml(user control)
<UserControl x:Class="Bates_Writer.View.FolderBrowserDialogV"
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:Bates_Writer.View"
xmlns:vm="clr-namespace:Bates_Writer.ViewModel"
mc:Ignorable="d" Height="42.056" Width="679.439">
<UserControl.DataContext>
<vm:FolderBrowserDialogVM>
</vm:FolderBrowserDialogVM>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="127*"/>
<ColumnDefinition Width="506*"/>
<ColumnDefinition Width="110*"/>
</Grid.ColumnDefinitions>
<Label Content="Folder Location: " FontSize="14" VerticalContentAlignment="Center"/>
<TextBox Grid.Column="1" FontSize="14" Margin="5,5,5,5" VerticalContentAlignment="Center" Text="{Binding SelectedPath}"/>
<Button Command="{Binding OpenCommand}" Content="Browse" Grid.Column="2" FontSize="14" Margin="5,5,5,5" Cursor="Hand"/>
</Grid>
</UserControl>
I even uploaded the sample project here
It looks like you have two instances of FolderBrowserDialogVM. One instance is DataContext for your FolderBrowserDialogV(here you have bound the command, but not the ObservableCollection) and another one for your FilesView(here you have bound ObservableCollection, but not the command, so your ObservableCollection will not be changed in this instance). For it works in your case, you should use(bind to) the same instance.
Delete from your UserControls:
<UserControl.DataContext>
<vm:FolderBrowserDialogVM>
</vm:FolderBrowserDialogVM>
</UserControl.DataContext>
Create an instance of your ViewModel in resources of your main window and set this instance as DataContext for your Usercontrols:
<MainWindow
xmlns:vm="clr-namespace:Bates_Writer.ViewModel" .. >
<MainWindow.Resources>
<vm:FolderBrowserDialogVM x:Key="vmInstance"/>
</MainWindow.Resources>
<StackPanel>
<YourUserControl1 DataContext="{StaticResource vmInstance}"/>
<YourUserControl2 DataContext="{StaticResource vmInstance}"/>
</StackPanel>
</MainWindow>
I have the following WPF XAML:
<Window x:Class="ListBoxTesting.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>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Name="ListBoxOne" Grid.Row="0" ItemsSource="{Binding ListOne}" DisplayMemberPath="NameOne"/>
<ListBox Name="ListBoxTwo" Grid.Row="1" ItemsSource="{Binding ListTwo}" DisplayMemberPath="NameTwo"/>
</Grid>
</Window>
With this code:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ListBoxTesting
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public class TypeOne
{
public string NameOne { get; set; }
public TypeOne(string name)
{
NameOne = name;
}
}
public class TypeTwo
{
public string NameTwo { get; set; }
public TypeTwo(string name)
{
NameTwo = name;
}
}
public ObservableCollection<TypeOne> ListOne { get; set; }
public ObservableCollection<TypeTwo> ListTwo { get; set; }
public MainWindow()
{
InitializeComponent();
ListOne = new ObservableCollection<TypeOne>();
ListOne.Add(new TypeOne("Mike"));
ListOne.Add(new TypeOne("Bobby"));
ListOne.Add(new TypeOne("Joe"));
ListTwo = new ObservableCollection<TypeTwo>();
ListTwo.Add(new TypeTwo("Mike"));
ListTwo.Add(new TypeTwo("Bobby"));
ListTwo.Add(new TypeTwo("Joe"));
}
}
}
But for some reason I can't see any rows in my UI once I start the project up in debug mode. Any ideas why?
You need to set DataContext to code behind to resolve your bindings:
May be in code behind:
public MainWindow()
{
InitializeComponent();
DataContext = this; <--- HERE
.....
}
OR in XAML:
<Window x:Class="ListBoxTesting.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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"> <-- HERE