i have one wpf window called usermanagement and there is a listbox showing all the users, i have one button in usermanagement window called add user and when i click on that new window opens called adduser, in this window there are input fields to add new user, what i need when i save data and this adduser window close then the usermanagement window update the listbox, means users again update (the new added user should show there after adding). at the moment i needed to open the usermanagement window again to see the new added user. Thanks!
here is the code below
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.Shapes;
using System.Collections;
using Model;
namespace Views
{
/// <summary>
/// Interaction logic for frmUserManagement.xaml
/// </summary>
public partial class frmUserManagement : Window
{
public frmUserManagement()
{
InitializeComponent();
}
public void window_loaded(object sender, RoutedEventArgs e)
{
load_users();
}
public void load_users()
{
RST_DBDataContext conn = new RST_DBDataContext();
var users = (from s in conn.TblUsers
select s.UserName).ToList();
Login_Names.ItemsSource = users;
}
private void add_user(object sender, RoutedEventArgs e)
{
adduser AddUserWindow = new adduser();
AddUserWindow.ShowDialog();
}
}
}
in xaml file there is
<Grid>
<ListBox Name="Login_Names" HorizontalAlignment="Left" Height="337" Margin="10,47,0,0" Padding="0,0,0,0" VerticalAlignment="Top" Width="156">
<Button Content="Add" HorizontalAlignment="Left" Margin="10,404,0,0" VerticalAlignment="Top" Width="75" Click="add_user"/>
</Grid>
Do insert operations inside main window (UserManagmentWindow):
UserManagmentWindow.cs:
// Inside add button handler open adduser window as dialog box...
var result = adduser.ShowDialog();
if(result == true){
// user pressed OK button...
// insert new user in database
// refresh UserManagmentWindow
}
Post your code if you need more details...
You can declare an event in Your AddUser window, and trigger the event when you press the button.
First define your EventArgs child class
public class AddUserEventArgs : EventArgs
{
public User AddInfo { get; private set; }
public AddUserEventArgs(User info)
{
this.AddInfo = info;
}
}
In your AddUser class:
public event EventHandler<AddUserEventArgs> AddedUser;
private void Button_Click(Object sender, RoutedEventArgs)
{
User info = new User();
// Realize your validation here...
// If validation is Okay, then..
if (OK)
{
if (this.AddedUser != null)
this.AddedUser(this, new AddUserEventArgs(info));
this.Close();
}
}
In your UserManagement class:
var window = new AddUserWindow();
window.AddedUser += (sender, e) =>
{
// Add the info to your ObservableCollection.
this.collection.Add(e.AddInfo);
}
window.ShowDialog();
Related
I am working on a to-do list application for a project. I would like to change the value of a string in an observableCollection. I am able to change the string in the same window but I would like to change the value from a textbox in a secondary window.
So what I tried to do was is change a string in the first window by using a textbox in the second window. By doing the way I have listed below it just blanks out the item I am trying to edit.
I would like to take the test from the textbox in the second window and use it to modify the taskName in the first window. Below I am going to include my code for the two c# files for the windows.
This is the main window but it is called DemoMainWindow:
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.Shapes;
using ToDoList.ViewModels;
using ToDoList.Model;
namespace ToDoList
{
/// <summary>
/// Interaction logic for DemoMainWindow.xaml
/// </summary>
public partial class DemoMainWindow : Window
{
private ViewModel _viewModel;
public string? EditedTaskName { get; set; }
public DemoMainWindow()
{
InitializeComponent();
TxtUCEnteredTask.txtLimitedInput.Text = "Do the dishes";
_viewModel = new ViewModel();
DataContext = _viewModel;
}
private void BtnAddTask_Click(object sender, RoutedEventArgs e)
{
_viewModel.Tasks.Add(new TaskModel() { TaskName = TxtUCEnteredTask.txtLimitedInput.Text });
}
private void BtnDeleteTask_Click(object sender, RoutedEventArgs e)
{
if(LstBoxTasks.SelectedItem != null)
{
#pragma warning disable CS8604 // Possible null reference argument.
_ = _viewModel.Tasks.Remove(item: LstBoxTasks.SelectedItem as TaskModel);
#pragma warning restore CS8604 // Possible null reference argument.
}
}
private void BtnHelp_Click(object sender, RoutedEventArgs e)
{
HelpWindow helpWindow = new HelpWindow();
helpWindow.Show();
}
private string? GetEditedTaskName()
{
return EditedTaskName;
}
private void BtnEditTask_Click(object sender, RoutedEventArgs e)
{
if (LstBoxTasks.SelectedItem != null)
{
EditWindow editWindow = new EditWindow();
editWindow.Show();
//_viewModel.Tasks[LstBoxTasks.SelectedIndex].TaskName = editedTaskName;
}
}
}
}
This is the code for the C# file of the second window:
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.Shapes;
namespace ToDoList
{
/// <summary>
/// Interaction logic for EditWindow.xaml
/// </summary>
public partial class EditWindow : Window
{
public EditWindow()
{
InitializeComponent();
var DemoMainWindow = this.DataContext;
}
private void BtnEdit_Click(object sender, RoutedEventArgs e)
{
((DemoMainWindow)Application.Current.MainWindow).EditedTaskName = EditTextBox.Text;
// _viewModel.Tasks[LstBoxTasks.SelectedIndex].TaskName = TxtUCEnteredTask.txtLimitedInput.Text;
}
}
}
When you create the view, create it's viewmodel before so that you can set the view's datacontext:
EditWindowViewModel vm = new EditWindowViewModel();
EditWindow editWindow = new EditWindow()
{
DataContext = vm;
};
editWindow.Show();
StringToChange = vm.EditBoxTextProperty;
Create a bindable property for stroing the editbox's text using INotifyPropertyCganged in the EditWindowViewModel (see this):
private string _editBoxTextProperty;
public string EditBoxTextProperty
{
get => _editBoxTextProperty;
set
{
if (_editBoxTextProperty != value)
{
_editBoxTextProperty = value;
OnPropertyChanged();
}
}
}
In the EditWindow xaml, use binding to connect the editbox's text value to your EditBoxTextProperty.
<TextBox Text="{Binding Path=EditBoxTextProperty}"/>
Usefull links to build a proper WPF application: DataBinding MVVM Pattern
What I have found to be useful when having to interact between elements on different windows is using x:FieldModifier
<TextBox x:Name="textbox" x:FieldModifier="public" />
You can then access the element through the instance of you window
it will appear as WindowInstanceName.textbox
I want to execute a method on TextChange event and for specific text, I want to do something and close the window using MVVM
for example on this part of my code I want to close my window:
if (text.Equals("12345"))
{
//Exit from window
}
I know how can I do it from a button using a command, As I have in the code of the next example.
But how can I pass the window to a running property that binds to a text of the textbox?
or there is another way to close form?
I want to do it on the ViewModel and not the code behind to write my code as MVVM pattern close that I can
my XAML code :
<Window x:Class="PulserTesterMultipleHeads.UserControls.TestWindows"
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:PulserTesterMultipleHeads.UserControls"
mc:Ignorable="d"
Name="MainTestWindow"
Title="TestWindows" Height="450" Width="800">
<Grid>
<StackPanel>
<TextBox Text="{Binding Text,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button Command="{Binding EndTestExit}"
CommandParameter="{Binding ElementName=MainTestWindow}">Exit</Button>
</StackPanel>
</Grid>
</Window>
the code behind XAML :
using PulserTesterMultipleHeads.Classes;
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.Shapes;
namespace PulserTesterMultipleHeads.UserControls
{
/// <summary>
/// Interaction logic for TestWindows.xaml
/// </summary>
public partial class TestWindows : Window
{
public TestWindows()
{
InitializeComponent();
DataContext = new TestWindowsMV();
}
}
}
View Model code :
using PulserTester.ViewModel.Base;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace PulserTesterMultipleHeads.Classes
{
public class TestWindowsMV : INotifyPropertyChanged
{
private string text;
public string Text {
get {
return text;
} set {
text = value;
if (text.Equals("12345"))
{
//Exit from window
}
}
}
/// <summary>
/// for the example
/// </summary>
private ICommand _EndTestExit;
public ICommand EndTestExit
{
get
{
if (_EndTestExit == null)
{
_EndTestExit = new GenericRelayCommand<Window>((window) => EndTestExitAction(window));
}
return _EndTestExit;
}
}
private void EndTestExitAction(Window window)
{
window.Close();
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
************************* Edit as the answer ****************************
The change in ModelView :
public string Text {
get {
return text;
} set {
text = value;
if (text.Equals("12345"))
{
CloseAction();
}
}
}
The change in code behind:
public TestWindows()
{
InitializeComponent();
DataContext = new TestWindowsMV();
if (((TestWindowsMV)DataContext).CloseAction == null)
((TestWindowsMV)DataContext).CloseAction = new Action(this.Close);
}
It is kind of hard with MVVM but what you can do is create an Event inside of your ViewModel and attach a function that will close your window in code behind of your View. Then you will be able to call your event inside of ViewModel whenever you need to close the window (or do something else that is more convenient to do in View rather than in ViewModel)
The cleanest way to close a View from the ViewModel is to use an attached property
public static class perWindowHelper
{
public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached(
"CloseWindow",
typeof(bool?),
typeof(perWindowHelper),
new PropertyMetadata(null, OnCloseWindowChanged));
private static void OnCloseWindowChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
if (!(target is Window view))
return;
if (view.IsModal())
view.DialogResult = args.NewValue as bool?;
else
view.Close();
}
public static void SetCloseWindow(Window target, bool? value)
{
target.SetValue(CloseWindowProperty, value);
}
public static bool IsModal(this Window window)
{
var fieldInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic);
return fieldInfo != null && (bool)fieldInfo.GetValue(window);
}
}
which you can then bind to an appropriate property in your ViewModel
<Window
x:Class="...
vhelp:perWindowHelper.CloseWindow="{Binding ViewClosed}">
private bool? _viewClosed;
public bool? ViewClosed
{
get { return _viewClosed; }
set { Set(nameof(ViewClosed), ref _viewClosed, value); }
}
More details on my recent blog post.
You have almost everything you need already implemented. All you really need to do is call private void EndTestExitAction(Window window) from your setter and supply window its value during construction:
public string Text {
get {
return text;
} set {
text = value;
if (text.Equals("12345"))
{
EndTestExitAction(window)
}
}
}
where window is a property of your View Model.
I'm working on a C# WPF project in Visual Studio in which I allow the user to add a user control (with two text boxes in the code below), which creates a class (with data stored within the user control so that multiple user controls can be created with a single button) and adds it to a list stored in the main window file to be written to a csv file later. I'm getting the following error when I hit the button to create a new user control:
An unhandled exception of type 'System.NullReferenceException' occurred in MasterListErrorTest.exe
Additional information: Object reference not set to an instance of an object.
I created a simplified version of my project that contains just the elements needed to reproduce the error. Here's all my code so you can plug it straight in and get the error for yourself. What am I doing wrong?
MainWindow.xaml:
<Window x:Class="MasterListErrorTest.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:MasterListErrorTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel x:Name="UserControlContainer" HorizontalAlignment="Left" Height="216" Margin="24,83,0,0" VerticalAlignment="Top" Width="471" Orientation="Horizontal"/>
<Button x:Name="CreateNewControl" Content="Create New" HorizontalAlignment="Left" Margin="76,37,0,0" VerticalAlignment="Top" Width="75" Click="CreateNewControl_Click"/>
<Button x:Name="GiveStringFromList" Content="Give String" HorizontalAlignment="Left" Margin="360,37,0,0" VerticalAlignment="Top" Width="75" Click="GiveStringFromList_Click"/>
</Grid>
</Window>
MainWindow.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 MasterListErrorTest
{
public partial class MainWindow : Window
{
int MultipleTextBoxControlID = 1;
public MainWindow()
{
InitializeComponent();
}
public static class TextBoxControlList
{
public static List <MultipleTextBoxControl.TextBoxData> MasterDataList;
}
private void CreateNewControl_Click(object sender, RoutedEventArgs e)
{
MultipleTextBoxControl newUserControl = new MultipleTextBoxControl(MultipleTextBoxControlID);
UserControlContainer.Children.Add(newUserControl);
}
private void GiveStringFromList_Click(object sender, RoutedEventArgs e)
{
foreach (MultipleTextBoxControl.TextBoxData textBoxPanel in TextBoxControlList.MasterDataList)
{
List<string> userControlLine = new List<string>();
userControlLine.Add(textBoxPanel.Identifier.ToString());
userControlLine.Add(textBoxPanel.TextBox1Data);
userControlLine.Add(textBoxPanel.TextBox2Data);
MessageBox.Show(string.Join(",", userControlLine));
}
}
}
}
MultipleTextBoxControl.xaml:
<UserControl x:Class="MasterListErrorTest.MultipleTextBoxControl"
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:MasterListErrorTest"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="300">
<Grid>
<TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="23" Margin="0,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" TextChanged="textBox1_TextChanged"/>
<TextBox x:Name="textBox2" HorizontalAlignment="Left" Height="23" Margin="153,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" TextChanged="textBox2_TextChanged"/>
</Grid>
</UserControl>
MultipleTextBoxControl.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 MasterListErrorTest
{
public partial class MultipleTextBoxControl : UserControl
{
TextBoxData newTextBoxGroup = new TextBoxData();
public MultipleTextBoxControl(int identifier)
{
InitializeComponent();
newTextBoxGroup.Identifier = identifier;
MainWindow.TextBoxControlList.MasterDataList.Add(newTextBoxGroup);
}
public class TextBoxData
{
public int Identifier { get; set; }
public string TextBox1Data { get; set; }
public string TextBox2Data { get; set; }
public TextBoxData()
{
TextBox1Data = "Unchanged Textbox 1";
TextBox2Data = "Unchanged Textbox 2";
}
}
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
newTextBoxGroup.TextBox1Data = textBox1.Text;
}
private void textBox2_TextChanged(object sender, TextChangedEventArgs e)
{
newTextBoxGroup.TextBox2Data = textBox2.Text;
}
}
}
Change your TextBoxControlList class to :
public static class TextBoxControlList
{
public static List <MultipleTextBoxControl.TextBoxData> MasterDataList;
static TextBoxControlList() {
MasterDataList = new List<MultipleTextBoxControl.TextBoxData>();
}
}
A better way to do :
Refactor#1
MultipleTextBoxControl.xaml.cs
public partial class MultipleTextBoxControl : UserControl
{
TextBoxData _newTextBoxGroup;
public TextBoxData TextBoxGroup { get { return _newTextBoxGroup; } }
public MultipleTextBoxControl(int identifier)
{
InitializeComponent();
_newTextBoxGroup = new TextBoxData(identifier);
}
public class TextBoxData
{
public int Identifier { get; set; }
public string TextBox1Data { get; set; }
public string TextBox2Data { get; set; }
public TextBoxData(int identifier)
{
Identifier = identifier;
TextBox1Data = "Unchanged Textbox 1";
TextBox2Data = "Unchanged Textbox 2";
}
}
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
_newTextBoxGroup.TextBox1Data = textBox1.Text;
}
private void textBox2_TextChanged(object sender, TextChangedEventArgs e)
{
_newTextBoxGroup.TextBox2Data = textBox2.Text;
}
}
MainWindow.cs
public partial class MainWindow : Window
{
int MultipleTextBoxControlID = 1;
public static List<MultipleTextBoxControl.TextBoxData> MasterDataList;
static MainWindow() {
MasterDataList = new List<MultipleTextBoxControl.TextBoxData>();
}
public MainWindow()
{
InitializeComponent();
}
private void CreateNewControl_Click(object sender, RoutedEventArgs e)
{
MultipleTextBoxControl newUserControl = new MultipleTextBoxControl(MultipleTextBoxControlID);
UserControlContainer.Children.Add(newUserControl);
MasterDataList.Add(newUserControl.TextBoxGroup);
}
private void GiveStringFromList_Click(object sender, RoutedEventArgs e)
{
foreach (MultipleTextBoxControl.TextBoxData textBoxPanel in MasterDataList)
{
List<string> userControlLine = new List<string>();
userControlLine.Add(textBoxPanel.Identifier.ToString());
userControlLine.Add(textBoxPanel.TextBox1Data);
userControlLine.Add(textBoxPanel.TextBox2Data);
MessageBox.Show(string.Join(",", userControlLine));
}
}
}
If you put a try/catch exception block around the code in your CreateNewControl_Click then the caught exception will give you more information about what's going on. The StackTrace says this:
at GuiTest.MultipleTextBoxControl..ctor(Int32 identifier) in c:\Dev\GuiTest\MultipleTextBoxControl.xaml.cs:line 31
at GuiTest.MainWindow25.CreateNewControl_Click(Object sender, RoutedEventArgs e) in c:\Dev\GuiTest\MainWindow25.xaml.cs:line 43
So the most recent item on the list is MultipleTextBoxControl.xaml.cs line 31:
MainWindow25.TextBoxControlList.MasterDataList.Add(newTextBoxGroup);
Placing a breakpoint here and examining the contents reveals that MasterDataList is null, because you're not initializing it in TextBoxControlList:
public static List <MultipleTextBoxControl.TextBoxData> MasterDataList;
So do so:
public static List <MultipleTextBoxControl.TextBoxData> MasterDataList = new List<MultipleTextBoxControl.TextBoxData>();
Personally I would strongly advise against the use of static classes members, especially in cases like this, but either way this is the answer to your question.
EDIT:
I totally agree with the main points that AnjumSKhan is making, although I personally would go about it using Inversion of Control (IoC). What you're really trying to do is get the child controls to register their data in a way that the Main Window code can access later. As AnjumSKhan points out the children shouldn't know anything about their parent, but you should also be able to create and unit test this behaviour in your child class without needing to create a parent. Inversion of control involves passing in an interface to the child that it should use to register itself, a very simple example might be this:
public interface IDataRegistrationService
{
void Register(MultipleTextBoxControl.TextBoxData data);
}
Your child window can accept a reference to such a service in its constructor and do the registration as it was before but using this service instead:
public MultipleTextBoxControl(int identifier, IDataRegistrationService service)
{
InitializeComponent();
newTextBoxGroup.Identifier = identifier;
service.Register(newTextBoxGroup); // <---------
}
Your MainWindow class can now inherit from this and pass a reference to itself in when the child is created (note I've also made MasterDataList a regular property of MainWindow):
public static List <MultipleTextBoxControl.TextBoxData> MasterDataList = new List<MultipleTextBoxControl.TextBoxData>();
public void Register(MultipleTextBoxControl.TextBoxData data)
{
MasterDataList.Add(data);
}
private void CreateNewControl_Click(object sender, RoutedEventArgs e)
{
MultipleTextBoxControl newUserControl = new MultipleTextBoxControl(MultipleTextBoxControlID, this);
UserControlContainer.Children.Add(newUserControl);
}
By doing this you've formalized the relationship between the parent and child and prevented yourself or others adding more relationships between the two that might be difficult to untangle or change later. You're also now in a position where you can create an instance of your child and use a mocked object (e.g. using the Moq library) to test that it behaves as expected. And by maintaining good separation of concerns you have the freedom to pass in any service you want...maybe later on you'll decide that you need multiple panels with one server per panel.
The one downside to IoC is that you wind up passing references to service provider all over your project, with middle layers keeping references to objects higher up the hierarchy for the sole purpose of passing them lower down. This is what Dependency Injection frameworks solve (e.g. Ninject). They get rid of all that parameter passing and your final code winds up looking something like this:
public partial class MultipleTextBoxControl : UserControl
{
// this gets created by the DI framework, with identifier set automatically
[Inject] private TextBoxData newTextBoxGroup { get; set; }
// this get injected automatically when the class is create
[Inject] private IDataRegistrationService DataService {get; set;}
public MultipleTextBoxControl()
{
InitializeComponent();
}
// this gets called immediately after the constructor
public void Initialize()
{
// and you do any custom initialization here, using injected components
this.DataService.Register(newTextBoxGroup);
}
I used semiglobal reference to MainWindow in user control like this:
public partial class MainWindow : Window
{
public static MainWindow Me = null;
public MainWindow()
{
Me = this;
InitializeComponent();
Then I used this "Me" in User control:
public partial class UserControlTable : UserControl
{
DataCenterSender m_DataCenterSender = new DataCenterSender(MainWindow.Me.Get_DataCenter());
And had a Null Pointer in XAML Editor.
Looks like constructor of MainWindow has not been called in XAML editor and since MainWindow.Me==null in this case User Control ctor failed.
here is the problem i am encountering, i will use the class names in my demo to describe my problem:
i have to show something on a datagrid, as a view of Item. but the Item itself is a wrapper class of the real data source, Dummy.
Container is the manager of all Dummy's, it will call Poll periodically(in my demo, 1s) and it internally queries the remote server to decide the current value(in demo, i simply flip the boolean). and after that, Container will go through all Item's and Notify them changes may be made.
but my problem is, the if branch in Notify called by Container won't get in, since PropertyChanged is null! but if i change it in the DataGrid, i.e. dg, by double click, this event gets subscribed. so my problem is, how can i successfully notify in Container? is there any a way to make DataGrid subscribe to PropertyChanged all the time?
btw, if i use a debugger to get in, they are null too.
Following is my demo code:
xaml:
<Window x:Class="WpfApplication6.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>
<DataGrid x:Name="dg" Margin="0,0,0,35"/>
<Button Content="Button" Margin="0,0,10,10" Height="20" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="75" Click="Button_Click"/>
</Grid>
</Window>
c# part:
using System;
using System.Collections.Generic;
using System.ComponentModel;
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;
using System.Windows.Threading;
namespace WpfApplication6
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Container _container;
public MainWindow()
{
InitializeComponent();
_container = new Container();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
dg.ItemsSource = _container.GetItems();
}
}
public class Dummy
{
public Dummy()
{
_done=false;
}
private bool _done;
public bool Done {
get { return _done; }
// set will trigger something remotely too.
set { _done = value; }
}
public void Poll() { _done=!_done;}
}
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Dummy dummy;
public Item(Dummy dum)
{
dummy = dum;
}
public bool Done
{
get { return dummy.Done; }
set
{
dummy.Done = value;
Notify();
}
}
public void Notify()
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Done"));
}
}
public class Container
{
private List<Dummy> dummies;
private DispatcherTimer _updateTimer;
public Container()
{
dummies = (from i in Enumerable.Range(0, 10)
select new Dummy()).ToList();
}
private IEnumerable<Item> items;
public Item[] GetItems()
{
if (_updateTimer != null)
_updateTimer.Stop();
items=dummies.Select(x => new Item(x));
_updateTimer = new DispatcherTimer();
_updateTimer.Interval = TimeSpan.FromSeconds(1);
_updateTimer.Tick += (_1, _2) =>
{
foreach (var item in dummies)
{
item.Poll();
}
foreach (var item in items)
{
item.Notify();
}
};
_updateTimer.Start();
return items.ToArray();
}
}
}
I wanna do a very simple thing using Expression Blend (SketchFlow).
I want to have a button in a screen that when it's pressed for more than 3 seconds it should go to another screen.
I thought about using this (green answer):
Button Long Click
within the MouseLeftButtonDown but I don't know how to change to another screen using c# code...just by setting the "Navigate to" option.
So if anyone could tell me how to set a long click behavior in a button so it changes to a new screen it would be great.
Here is a similar but simpler class you can use:
using System;
using System.Collections.Generic;
using System.Text;
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.Shapes;
using System.Windows.Interactivity;
using System.Windows.Threading;
namespace SilverlightApplication14
{
public class LongClickButton : Button
{
public event EventHandler LongClick;
public static DependencyProperty HowLongProperty = DependencyProperty.Register("HowLong", typeof(double), typeof(LongClickButton), new PropertyMetadata(3000.0));
public double HowLong
{
get
{
return (double)this.GetValue(HowLongProperty);
}
set
{
this.SetValue(HowLongProperty, value);
}
}
private DispatcherTimer timer;
public LongClickButton()
{
this.timer = new DispatcherTimer();
this.timer.Tick += new EventHandler(timer_Tick);
}
private void timer_Tick(object sender, EventArgs e)
{
this.timer.Stop();
// Timer elapsed while button was down, fire long click event.
if (this.LongClick != null)
{
this.LongClick(this, EventArgs.Empty);
}
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
this.timer.Interval = TimeSpan.FromMilliseconds(this.HowLong);
this.timer.Start();
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
this.timer.Stop();
}
}
}
With this, you can use any of the standard behaviors in Blend along with the new LongClick event. Set the HowLong property to the number of ms you want (default is 3000) and then use an eventtrigger set to LongClick to trigger your navigation:
<local:LongClickButton Margin="296,170,78,91">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LongClick">
<ei:ChangePropertyAction PropertyName="Background" TargetName="LayoutRoot">
<ei:ChangePropertyAction.Value>
<SolidColorBrush Color="#FFFF1C1C"/>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</local:LongClickButton>