I have a UserControl, which is basically a wrapper for a GridView that needs to send a message every time a cell content (of the GridView) is changed. Usually, that could be solved like this:
private void MainDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
var editingTextBox = e.EditingElement as TextBox;
doSomething(editingTextBox.Text);
}
The problem is that I don't know the type of EditingElement (which comes as a FrameworkElement) so I cannot do the conversion. And in that moment, the currentCell.SelectedValue is still the original value. I also don't have control over the model (where I could implement INotifyPropertyChanged and use it to catch the changes).
Is there some simple way I am missing? Or how would you go about implementing this? Thank you for any suggestions.
Wrapping your model within a CollectionView and use this for the binding.
myCollectionView = (CollectionView)
CollectionViewSource.GetDefaultView(rootElem.DataContext);
This will provide you a INotifyPropertyChanged interface.
Update
Sorry my first answer was somewhat misleading.
If you're not able to change the model, you should create a view model. The view model, implementing INotifyPropertyChanged can provide the needed change events without any knowledge of the current view. This way the view doesn't depend directly on the model.
Futher reading:
The role of the model in MVVM
I have found a very simple solution (can't belive I haven't seen that one) composed of catching two events from the DataGrid.
Here is the code:
private object changedRow;
private void MainDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
changedRow = e.Row.Item;
}
private void MainDataGrid_CurrentCellChanged(object sender, EventArgs e)
{
if (changedRow != null)
{
// do something with the edited row here
changedRow = null;
}
}
Related
For some time i'm trying to create navigation service in my app while studying Xamarin. My approach here don't foolow mvvm desing pattern but at first i treid to have functionality working on which i will study further. I figured out how to navigate using ItemSelected property in listview through event fired in code-behind file and pass parameter from object placed in listview as a string. I want to changed it for more custom way by adding tap gesture on image not (imagebutton don't even fired event for new page when i added tap gesture on it in xaml file)
This worked for itemselected:
private async void SampleItem_Tapped(object sender, ItemTappedEventArgs e)
{
var details = e.Item as SampleModel;
await Navigation.PushAsync(new Page1(details));
}
new page codebehind:
public Page1(SimpleModel sample)
{
InitializeComponent();
var pageServices = new PageServices();
BackgroundImage = sample.SampleMainBackgroundImage;
}
This is where i need your help : ( app throwing exception when i assing as constructor of new page a string property and bound it to my string backgroundimagebinding (object reference not set to an instance of an object new page) )
private async void SampleIcon_Tapped(object sender, ItemTappedEventArgs e)
{
var details = e.Item as SampleModel;
await Navigation.PushAsync(newPage1(details));
}
e.Item on code below would be the visual element which I think is the image. Hence, it can't be casted as SampleModel.
private async void SampleIcon_Tapped(object sender, ItemTappedEventArgs e)
{
var details = e.Item as SampleModel;
await Navigation.PushAsync(newPage1(details));
}
It worked on ListView maybe because you've set the ItemsSource to a list of PageModels. Btw, It would be really helpful next time if you include all related codes. E.g. xaml part. Also, try out debugger tools so you can view the details of the objects passed on the method.
I dont know if is coincidence or not, but after updating Visual Studio 2013 with update 5 my listview doesnt update any more. Because i am not any mvvm wpf expert please for help.
App update List every time when USB plug in or out - this happening with help of backgroundworker and eventhandler.
my ViewModel
public ObservableCollection<Devices> devicelist { get; protected set; }
void DeviceInsertedEvent(object sender, EventArrivedEventArgs e)
{
fd.FindDevices();
RefreshList();
}
void DeviceRemovedEvent(object sender, EventArrivedEventArgs e)
{
fd.FindDevices();
RefreshList();
}
public void RefreshList()
{
devicelist = new ObservableCollection<Devices>();
devicelist.Clear();
foreach (var item in fd.devices)
{
devicelist.Add(item);
}
}
So, if device is pluged in or out, method FindDevices start (this loop just asking for information for every device) and at the end I get List of devices with all the informations. The I like to refresh list. "fd.devices" have right number of devices only WPF doesnt refresh.
If have anyone any question, please ask. And please help me with my issue :)
You need to Raise a Property changed event. This ripples up to the UI element and basically it refreshes what data it has.
Assuming the list is bound to from the UI?
If you don't already have a base view model class it might be worth looking at this:
http://www.galasoft.ch/mvvm
I have a CheckBox that has it's Checked property bound to a bool value. During the CheckedChanged event, some logic runs which uses the bool property on the data source.
My problem is the first time the CheckBox gets checked by the user, the bound data source does not get updated. Subsequent updates work fine though.
Here's some sample code for testing the problem. Just create a blank form and add a CheckBox to it.
public partial class Form1 : Form
{
private bool _testBool;
public bool TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool"));
checkBox1.CheckedChanged += new EventHandler(checkBox1_CheckedChanged);
}
void checkBox1_CheckedChanged(object sender, EventArgs e)
{
checkBox1.BindingContext[this].EndCurrentEdit();
Debug.WriteLine(TestBool.ToString());
}
}
The first time I check the box, the TestBool property remains at false, even though checkBox1.Checked is set to true. Subsequent changes do correctly update the TestBool property to match checkBox1.Checked though.
If I add a breakpoint to the CheckedChanged event and check out checkBox1.BindingContext[this].Bindings[0] from the immediate window, I can see that modified = false the first time it runs, which is probably why EndCurrentEdit() is not correctly updating the data source.
The same thing also occurs with using a TextBox and the TextChanged event, so this isn't limited to just CheckBox.Checked.
Why is this? And is there a generic common way of fixing the problem?
Edit: I know of a few workarounds so far, although none are ideal since they are not generic and need to be remembered everytime we want to use a Changed event.
setting the property on the datasource directly from the CheckedChanged event
finding the binding and calling WriteValue()
hooking up the bindings after the control has been loaded
I am more concerned with learning why this is happening, although if someone knows of a standard generic solution to prevent it from happening that does not rely on any special coding in the Changed event, I'd be happy with that too.
Controls usually want to go through their validation first before writing to the data source, so the writing of the value usually won't happen until you try to leave the control.
You can force the writing of the value yourself:
void checkBox1_CheckedChanged(object sender, EventArgs e) {
Binding b = checkBox1.DataBindings["Checked"];
if (b != null) {
b.WriteValue();
}
Debug.WriteLine(TestBool.ToString());
}
Apparently, the CheckedChanged event is too early in the process.
But you can leverage BindingComplete:
public partial class Form1 : Form
{
private Boolean _testBool;
public Boolean TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool", true, DataSourceUpdateMode.OnPropertyChanged));
checkBox1.DataBindings[0].BindingComplete += Form1_BindingComplete;
}
private void Form1_BindingComplete(Object sender, BindingCompleteEventArgs e)
{
Debug.WriteLine("BindingComplete: " + TestBool.ToString());
}
}
Note that the event will fire at startup as the initial bind linkage occurs. You will have to deal with that possible unintended consequence, but otherwise, it works on the first click and every click.
Also note that the true (format) is required in the Binding constructor to make the event fire.
The closest I can find for an explanation to this behavior is this 3rd party explanation
Basically, this is an issue of timing. The way binding works in DotNet
is actually very simple. There's no magic in the DotNet framework that
tells the BindingManager when something changes. What it does is, when
you bind to a property (such as CheckedValue) The BindingManager looks
for an event on the control called propertynameChanged (e.g.
"CheckedValueChanged"). This is the same event your code is hooking
into on your sample form.
When the control fires the event, the order in which the listeners
receive the event is arbitrary. There's no reliable way to tell
whether the BindingManager will get the event first or the Form will.
My CheckBox1_CheckChanged event is running before the BindingManager handles the changed event, so the data source hasn't been updated at this time.
My best guess as to why this only happens the first time is that the control isn't visible yet, so some code doesn't get run that should fix the order events get handled in. I've seen other posts about not being able to bind to non-visible items due to the handle not being created yet, and one answer states
Until the control is visible for the first time some back-end initialization never happens, and part of that initialization is enabling the data binding.
So I suspect that this is somehow related.
I can verify that if I attach the Changed handler later on such as during the Load event, it works as I would expect.
public partial class Form1 : Form
{
private bool _testBool;
public bool TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool"));
Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
checkBox1.CheckedChanged += new EventHandler(checkBox1_CheckedChanged);
}
void checkBox1_CheckedChanged(object sender, EventArgs e)
{
// Not needed anymore
//checkBox1.BindingContext[this].EndCurrentEdit();
Debug.WriteLine(TestBool.ToString());
}
}
Basically I have a longlistselector and it contains writings when one of them is selected I am going to navigate to a page and its going to show the all detailed information of this writing (article , date , image etc.)
Don't worry about the data, I checked and data contains the details all I need is how to send this Writing object (named data) with navigating.Or can you give any suggestion how can I do this but without using uri query
Thank you
private void longList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
LongListSelector selector = sender as LongListSelector;
Writing data = selector.SelectedItem as Writing;
NavigationService.Navigate(new Uri("/WritingPage.xaml", UriKind.Relative));
}
To pass an Object from one page to another PhoneApplicationservice is easiest way. Here is the simple example to pass Object from one page to another. Its tested.
private void longList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (PhoneApplicationService.Current.State.ContainsKey("Data"))
if (PhoneApplicationService.Current.State["Data"] != null)
PhoneApplicationService.Current.State.Remove("Data");
LongListSelector selector = sender as LongListSelector;
Writing data = selector.SelectedItem as Writing;
PhoneApplicationService.Current.State["Data"] = data ;
NavigationService.Navigate(new Uri("/WritingPage.xaml", UriKind.Relative));
}
//On second page
//I assume you want to Data on page load
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Writing data = new Writing ();
data =(Writing)PhoneApplicationService.Current.State["Data"]
PhoneApplicationService.Current.State.Remove("Data");
}
Are you coding in MVVM ?
If yes, you can send and register to an Event (published by your ViewModel linked to the LongList and subscribe this event in your ViewModel linked to WrittingPage.)
Simple and better way is to make "Writing" object scope Global.
Here, I added a class file to solution "Global.cs" and I declared "data" variable of "Writing" type.
public static Writing data = new Writing();
Use the same code you did and access the "data" variable here. I have done some minor change to your code.
private void longList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
LongListSelector selector = sender as LongListSelector;
Global.data = selector.SelectedItem as Writing;
NavigationService.Navigate(new Uri("/WritingPage.xaml", UriKind.Relative));
}
Now access "data" variable on "WritingPage.xaml" after you navigate to it. Use value of this variable and then make it null.
I'm having a hard time building a filtering system in Sitecore 7.
I have 2 sublayouts, on the same level of the page.
Sublayout A is a sidebar that contains a list of checkboxes and has an event that populates a List with the selected values.
Sublayout B displays a set of items.
What I would like to do, is send the populated List from sublayout A to sublayout B in order to filter the items list based on what the user selected.
I was able to do this by passing the data through Session, but this is not an optimal way of handling that data.
I've tried defining a property for sublayout A and loading the list there, but I can't get the exact instance of sublayout A from sublayout B in order to read the populated property.
Also, trying to Page.FindControl("IdOfSomeElementFromSublayoutA") always returns null in Sublayout B. Even though I've casted Page as the .aspx page that contains both Sublayouts.
I'm using Sitecore 7 Update 2.
Thanks a lot for your time.
The best way to do this is by raising (and subscribing to) events using the Sitecore.Events.Event class. Your sidebar sublayout would raise an event using something like the following within a button's click event handler:
Sitecore.Events.Event.RaiseEvent("YourEventName", new YourEventArgsClass { Property = "SomeValue" });
then in the other sublayout you would need to have the following set up in order to handle the event:
public partial class YourOtherSublayout : System.Web.UI.UserControl
{
private System.EventHandler eventHandlerRef;
protected void Page_Load(object sender, EventArgs e)
{
eventHandlerRef = EventHandlerMethod;
Sitecore.Events.Event.Subscribe("YourEventName", eventHandlerRef);
}
protected void Page_Unload(object sender, EventArgs e)
{
if (eventHandlerRef != null)
{
Sitecore.Events.Event.Unsubscribe("YourEventName", eventHandlerRef);
}
}
private void EventHandlerMethod(object sender, EventArgs e)
{
if (e != null)
{
//do stuff here
}
}
}
Note: it is important to keep the Page_Unload code there otherwise you will see the EventHandler method being called multiple times.