WinRT and MVVM Light V5 NavigationService - c#

I am developing a WinRT application using MVVM Light V5. I want to navigate from a page to another, and pass an object. I have a GridView and I want to catch the clicked item, so I created a RelayCommand, which does this:
private void ItemClickExecute(ItemClickEventArgs e)
{
navigationService.NavigateTo("AnotherPage", e.ClickedItem as MyObject);
}
This is working fine. My problem is to get this object from the "AnotherPage" ViewModel. How can I do that ?

Sorry I'm late for this answer but hope this helps.
You should create a ViewModel for the page you want to navigate to and set it as its DataContext. Then in that view model create a MyObject property.
After that override the OnNavigatedTo event of the page you want to navigate to.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var vm = (SecondPageViewModel)this.DataContext;
if (vm!=null)
{
var temp = e.Parameter as MyObject;
if (temp != null)
{
vm.MyObjectProperty = temp;
}
}
}
This should do it.
There are other more complicated(and more mvvm compliant) answers out there but that works for me.

Related

Caliburn Micro Screen Activated Events

I'm using Caliburn Micro and AvalonDock in my project. I try to handle events when screen was activated. My main view model is a 'Conductor.Collection.OneActive' and each tab "document" is a 'Screen'.
I have a function in my main view model like this:
public void CheckAndRegisterDocument(DocumentViewModel document)
{
DocumentViewModel exists = _documents.FirstOrDefault((d) => { return d.Equals(document); });
// if not exists - add it
if(exists == null) {
document.Activated += Document_Activated;
_documents.Add(document);
Items.Add(document);
}
// activate and set property object
ActivateItem(document);
Properties.PropertiesObject = document.Properties;
}
// document activated event handler
private void Document_Activated(object sender, ActivationEventArgs e) {
ActiveDocument = sender as DocumentViewModel;
}
But the function "Document_Activated" is not invoked. what am I doing wrong?
Instead of adding your document objects into a documents collection, add them to the already existing this.Items collection.
Also, each document object will need to inherit from Screen for it to participate.
That +should+ be enough to do the trick, but sometimes it can be necessary to tell Caliburn to "conduct" your viewmodels via ConductWith...
document.ConductWith(this)
there this is the current conductor viewmodel.

WPF - Binding events to class methods of Item in ItemControl

I'm a bit new to WPF/XAML (though I've learnt C#) and would really appreciate any help for my question. I did look around other posts and google for a while but I can't seem to find a satisfactory or detailed answer to get me going on with my project. Please look below for details. Thanks you in advance!
Objective
I have a class called Tile that consists of a few properties and an event handler.
I also have an ItemControl that has a button (as by the DataTemplate), and whose ItemSource is a collection of Tiles.
Now, I want to bind the "Click" event of the Button so as to invoke the Event Handler method defined in the class Tile.
In other words when I click the button of any item in the ItemControl, the method handler of the corresponding Tile instance (from the collection) must be invoked. How would I tackle this problem?
Below is the entire code, simplified to avoid distraction:
XAML
<Window x:Class="SampleWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<!-- Make a ItemControl for "Tile"s. -->
<ItemsControl x:Name="TileList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Wire the click event of this Button
to event handler in the Tile class. -->
<Button Content="Show"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
CODE-BEHIND
namespace SampleWPF
{
public partial class MainWindow : Window
{
ObservableCollection<Tile> tiles;
public MainWindow()
{
InitializeComponent();
// Adding some sample data for testing.
tiles = new ObservableCollection<Tile>();
tiles.Add(new Tile("Item 1"));
tiles.Add(new Tile("Item 2"));
TileList.ItemsSource = tiles;
}
}
public class Tile : INotifyPropertyChanged
{
public string Data
{ /* Accessors and PropertyNotifiers */ }
public Tile(string data)
{ /* Initializing and assigning "Data" */ }
// INotifyPropertyChanged implementation...
// { ... }
// This event handler should be bound to the Button's "Click" event
// in the DataTemplate of the Item.
public void ShowButton_Click(object sender, EventArgs e)
{
MessageBox.Show("Viewing item from: " + this.Data);
}
}
}
Hence, if I click the first "Show" button, the output should be "Viewing item from: Item 1" and if I click the second "Show" Button, the output should be "Viewing item from: Item 2".
So what is the recommended/efficient way to do this? Is my code inappropriate for this requirement?
Event handlers are the wrong approach - use Commands and more importantly MVVM.
As I can see that you are new (and probably from a WinForms or ASP.NET background) you should read this blog to understand how your thinking needs to change - this is the most important part to understand before tackling WPF: http://rachel53461.wordpress.com/2012/10/12/switching-from-winforms-to-wpfmvvm/
You should also read Kent Boogart's blog on how MVVM works from base principles: http://kentb.blogspot.co.uk/2009/03/view-models-pocos-versus.html
Let me start with some basics:
Don't assign itemsource in codeBehind - use Binding like this:
<Controll ItemSource="{Binding MyObservableCollection}"/>
There are many ways You can achieve this. I think that using this.Data is not the best solution for this.
For example if Your tail have ID or something You can assign this id to button CommandParameter like below
<Button CommanParameter="{Binding Path=ID}" Click="ShowButton_Click"/>
And then in Your button_click event u can 'catch' this like this:
public void ShowButton_Click(object sender, EventArgs e)
{
int ID = int.Parse(((Button)sender).CommandParameter.ToString());
}
EDIT
To use this binding You need to set DataContext. You can do this in ctor like this:
public MainWindow()
{
InitializeComponent();
// Adding some sample data for testing.
tiles = new ObservableCollection<Tile>();
tiles.Add(new Tile("Item 1"));
tiles.Add(new Tile("Item 2"));
// below You are setting a datacontext of a MainWindow to itself
this.DataContext = this;
}
ANOTHER EDIT
Let's assume Your tail class have property called ID. If You bound this ID to Button.CommandParameter You can later retrieve the tile with linq like this:
public void ShowButton_click(object sender, EventArgs e)
{
int MyId = int.Parse(((Button)sender).CommandParameter.ToString());
Tile TileIWasSearchingFor = (from t in tiles where t.ID == MyId select t).First();
// do something with tile You found
}
Well since my requirement was rather "simple", I've managed a work around, avoiding commands. Thanks to the answer here by MajkeloDev: https://stackoverflow.com/a/27419974/3998255 for guidance.
This is the final event handler:
public void ShowButton_Click(object sender, EventArgs e)
{
Tile requestingTile = (sender as Button).DataContext as Tile;
if(requestingTile != null)
MessageBox.Show("Viewing item from: " + this.Data);
// Or whatever else you want to do with the object...
}
Also, adding the ItemSource as a XAML attribute:
<ItemsControl x:Name="TileList" ItemsSource="{Binding tiles}">
And setting DataContext in constructor of MainWindow:
public MainWindow()
{
this.DataContext = this;
// Whatever else you want to do...
}
Well it works as required.

Filtering CollectionViewSource

I want to make a ComboBox bound to my data, with a filter. For that I've created a TextBox and a ComboBox. In the code behind I read a file and generate objects of class Channel that are stored as items of the ComboBox. Although the compiler throws no error the filtering doesn't work properly. If I write something the data is gone, if I erase, it's back. After trying and trying I've realized that if I started typing "myNamespace.myChannel" (Unico.Canal) the data remained, but don't filter. Strange behaviour, indeed. I suspect that I've put something in wrong place.
(for better understanding I've translated the code, Canal=Channel)
Here is the scheme of my code:
namespace Unico
{
public partial class ControlesArchivo : UserControl, INotifyPropertyChanged
{
public ControlesArchivo()
{
InitializeComponent();
}
public ObservableCollection<Channel> myListChannels //with INotifyPropertyChanged implemented. But I think I don't need it.
private void loadButton_Click(object sender, RoutedEventArgs e)
{
File loadedFile = new File();
loadedFile.read(); //Generates a bunch of data in lists.
foreach (Channel mychan in loadedFile.channels) //Just duplicating the data (maybe this can be avoided)
{
myListChannels.Add(mychan);
}
var view = CollectionViewSource.GetDefaultView(this.miListaDeCanales);
view.Filter = delegate(object o)
{
if (o.ToString().Contains(myTextBox.Text)) //Delicate place
{
return true;
}
return false;
};
myComboBox.ItemsSource = view;
DataContext = this;
}
private void myTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
((ICollectionView)myComboBox.ItemsSource).Refresh();
myComboBox.SelectedIndex = 0;
}
}
}
The data is bound in XAML with:
ItemsSource="{Binding view}"
EDIT: I think I know where is the problem: I'm not specifing the property to filter. I mean, what you see in the ComboBox is the property channelName of the class Channel listed in myListChannels. When I'm setting the filter, shouldn't I let know what I'm filtering? How could I write this? Thank you very much.
Yes your assumption is correct.
I'm assuming with your translations,
public ObservableCollection<Channel> myListChannels;
is actually
public ObservableCollection<Canal> miListaDeCanales;
with the class Canal in the namespace Unico
Update:
In your filter try using the property that is rendered in the ComboBox than use the ToString() on the object(o) if you've not overridden ToString() from System.Object.
try switching
if (o.ToString().Contains(myTextBox.Text))
to
if (((Canal)o).NameProperty.Contains(myTextBox.Text))
^^ that should fix your issue.
Do you have a DataTemplate for ComboBox.ItemTemplate in xaml. That will explain why you see the valid value rendered in the ComboBox, else all the ComboBoxItem's will also render as Unico.Canal

Catch "switching workspaces" event in MVVM

I have an application build with the MVVM pattern from Josh Smith (http://msdn.microsoft.com/en-us/magazine/dd419663.aspx).
When I have several workspaces opened in my app, I want to catch the event of switching workspaces/tabs so I can save the content of the current workspace first. I have looked throught WorkspaceViewModel and ViewModelBase, but I don't know how to add that EventHandler.
I have found a solution in another post, I just had to tweak it a little bit : What is the proper way to handle multiple datagrids in a tab control so that cells leave edit mode when the tabs are changed?
Basically I have added an EventHandler on PreviewMouseDown of my TabControl generating the different Workspaces.
private void TabControl_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
MainWindow_VM dc = (MainWindow_VM)this.DataContext;
if (IsUnderTabHeader(e.OriginalSource as DependencyObject))
//Do what need to be done before switching workspace
// in my case, switch the focus to a dummy control so the objectContext would save everything, even the currently focused textbox
}
private bool IsUnderTabHeader(DependencyObject control)
{
if (control is TabItem)
{
return true;
}
DependencyObject parent = VisualTreeHelper.GetParent(control);
if (parent == null)
return false;
return IsUnderTabHeader(parent);
}
You should be able to bind the "Current" item of the tab to a variable in the model. When this changes, do your work.

WP7 Silverlight - Change DataContext in runetime

how do I change the DataContext to another view model in runtime.
Now the data is not changed, after I run the click event:
public PivotPage1()
{
InitializeComponent();
DataContext = App.TeamDocViewModel;
}
private void Button_Click(object sender, EventArgs e)
{
DataContext = App.TaskViewModel;
}
Some suggestions?
First I think you have a typo: App.TaskViewMode should be App.TaskViewModel. Second: Your code should work. There might be problem with binding in your xaml file, would be nie if you post it here. (Maybe you bind to submembers like "User.Name" and don't implement INotifyPropertyChanged in your models)
What happens if you change DataContext = App.TaskViewModel; to DataContext = null; ?) Xaml is interesting to see.
The code you're written should be fine. It depends on what TaskViewMode is and how it works.
Assuming that it is based on the MainViewModel in the default Pivot project template. Are you calling LoadData() on it to populate the Items Collection?

Categories

Resources