CaliburnMicro Action does not fire inside data template - c#

I want to create some sort of filter, when user clicks the filter button from the app bar it will fire up a popup page with list picker in it. I've googled and tried quite a number of solutions but still cannot get it to work.
Here are my codes:
XAML (MainPageView.xaml)
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="PivotContentTemplate">
<phone:Pivot Margin="-12,0,0,0" Title="FOREX NEWS" Height="672">
<phone:PivotItem Header="filter" FontFamily="{StaticResource PhoneFontFamilySemiLight}" FontSize="32">
<StackPanel Margin="12,0,0,0">
<toolkit:ListPicker Header="currencies" SelectionMode="Multiple"
micro:Message.Attach="[Event SelectionChanged] = [Action OnCurrenciesChanged($eventArgs)]">
<sys:String>gbp</sys:String>
<sys:String>eur</sys:String>
<sys:String>usd</sys:String>
</toolkit:ListPicker>
</StackPanel>
</phone:PivotItem>
</phone:Pivot>
</DataTemplate>
<phone:PhoneApplicationPage.Resources>
...
Still inside MainPageView.xaml
<bab:BindableAppBar Grid.Row="2" Mode="Minimized">
<bab:BindableAppBarButton micro:Message.Attach="[Event Click] = [Action ShowFilter($view, $eventArgs]">
</bab:BindableAppBarButton>
</bab:BindableAppBar>
MainPageViewModel.cs
public void ShowFilter(object sender, RoutedEventArgs e)
{
var view= sender as MainPageView;
CustomMessageBox messageBox = new CustomMessageBox()
{
ContentTemplate = (DataTemplate)view.Resources["PivotContentTemplate"],
LeftButtonContent = "filter",
RightButtonContent = "cancel",
IsFullScreen = true // Pivots should always be full-screen.
};
messageBox.Dismissed += (s1, e1) =>
{
switch (e1.Result)
{
case CustomMessageBoxResult.LeftButton:
// Do something.
break;
case CustomMessageBoxResult.RightButton:
// Do something.
break;
case CustomMessageBoxResult.None:
// Do something.
break;
default:
break;
}
};
messageBox.Show();
}
public void OnCurrenciesChanged(SelectionChangedEventArgs e)
{
}
For your information, I am using Caliburn.Micro and WP Toolkit for the CustomMessageBox and ListPicker.
I received exception No target found for method OnCurrenciesChanged. I only receive the exception when I after I select few items in the list picker and click any of the buttons to save the change. Another thing is that the OnCurrenciesChanged does not get triggered at all.
I think (based on what I read so far) whenever the CustomMessageBox get called, the datacontext its operating at is no longer pointing to the MainPageViewModel thus it could not find the method. But I am not sure how to actually do this.
More details:
Exception happen after I click the left button (checkmark)
Updates
So far I have try the following:
<StackPanel Margin="12,0,0,0" DataContext="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"> //also tried with Self
and I also added this when I instantiate messageBox
var messageBox = new CustomMessageBox()
{
ContentTemplate = (DataTemplate)view.Resources["PivotContentTemplate"],
DataContext = view.DataContext, // added this
LeftButtonContent = "filter",
RightButtonContent = "cancel",
IsFullScreen = true
};
The idea is that when the messsagebox is created, the datacontext will be the same as when the view is instantiated. However, it seems that the datacontext does not get inherited by the PickerList

Ok so I managed to get this to work. The solution is not pretty and I think it beats the purpose of MVVM at the first place.
Based on http://wp.qmatteoq.com/first-steps-in-caliburn-micro-with-windows-phone-8-how-to-manage-different-datacontext/ , inside a template the DataContext will be different. So, I need to somehow tell ListPicker to use the correct DataContext.
The solution provided by link above doesn't work for me. I think it is because when ListPicker is called inside CustomMessageBox, MainPageViewModel is no longer available or it seems not to be able to find it as suggested by the exception. So as per above code example in the question, even if I set the correct DataContext to the CustomMessageBox, it does not get inherited somehow by the ListPicker.
Here is my solution:
var messageBox = new CustomMessageBox()
{
Name = "FilterCustomMessageBox", // added this
ContentTemplate = (DataTemplate)view.Resources["PivotContentTemplate"],
DataContext = view.DataContext,
LeftButtonContent = "filter",
RightButtonContent = "cancel",
IsFullScreen = true
};
In the XAML, I edited to this
<toolkit:ListPicker Header="currencies" SelectionMode="Multiple"
micro:Action.TargetWithoutContext="{Binding ElementName=FilterCustomMessageBox, Path=DataContext}"
micro:Message.Attach="[Event SelectionChanged] = [Action OnCurrenciesChanged($eventArgs)]">
It's ugly because both ViewModel and View need to explicitly know the Name. In WPF, you can just do something like this in the binding to inherit the DataContext of the parent/etc but this is not available for WP.
{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}
If anyone has better workaround, do let me know!

Related

UWP Swipeitem won't see any bindings

For some reason I can't use any binding with a UWP Xaml SwipeItem Control. I've tried it in many different ways and with different SwipeItem properties but every time it is null. What's even stranger is any type of x:Bind to any property and it will crash.
if anyone marks this:
SwipeItem XAML Binding ignored
as a duplicate question it isn't so don't do it or I'll freak out. That question wasn't even answered.
<SwipeControl>
<SwipeControl.LeftItems>
<SwipeItems Mode="Execute">
<SwipeItem Text="{Binding}" Background="{StaticResource MIIGreen}" BehaviorOnInvoked="Close"/>
</SwipeItems>
</SwipeControl.LeftItems>
<SwipeControl.RightItems>
<SwipeItems Mode="Execute">
<SwipeItem Background="{StaticResource MIIRed}" BehaviorOnInvoked="Close" Command="{StaticResource CommandEnclosureNotInstalled}" CommandParameter="{Binding}"/>
</SwipeItems>
</SwipeControl.RightItems>
</SwipeControl>
the DataContext is just a simple DataModel and all other controls are binding fine. the command is from a staticresource and the command is firing just fine. in this example any combination of Binding or x:Bind either does nothing or crashes when trying to bind ANYTHING to Text or CommandParamter properties. There has to be something wrong with SwipItem controls, I need a way to pass the DataContext through the CommandParameter.
SwipeControl is not a standard itemControl, it does not have a dataTemplate, so SwipeItem can't find the DataContext of the parent view, so you can't directly use Binding directly in xaml. It seems you can only use Binding in code.(Below I give example of LeftItems).
in xaml:
<SwipeControl Margin="50" LeftItems="{Binding leftItems}">
</SwipeControl>
in cs:
public SwipeItems leftItems { get; set; }
public MainPage()
{
this.InitializeComponent();
SwipeItem leftItem = new SwipeItem();
Binding myBinding = new Binding();
myBinding.Source = viewmodel;
myBinding.Path = new PropertyPath("MyText"); //the property in viewmodel
BindingOperations.SetBinding(leftItem, SwipeItem.CommandParameterProperty, myBinding);
BindingOperations.SetBinding(leftItem, SwipeItem.TextProperty, myBinding);
Binding commandBinding = new Binding();
commandBinding.Source = new FavoriteCommand(); //command class
BindingOperations.SetBinding(leftItem, SwipeItem.CommandProperty, commandBinding);
leftItems = new SwipeItems() {
leftItem
};
this.DataContext = this;
}

WPF ListView binding to the collection

I implemented a collection of Hyperlink elements using WPF:
var controlLinks = new List<Hyperlink>();
if (issueLinks != null)
{
foreach (var link in issueLinks)
{
var tempLink = new Hyperlink()
{
NavigateUri = new Uri(link.link)
};
controlLinks.Add(tempLink);
}
}
ListIssueLinks.ItemsSource = controlLinks;
Collections is successfuly filled, now I link ListIssueLinks view to this collection.
<ListView Name="ListIssueLinks" Height="100" >
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
Here I've got a problem, the issue is I'm new to WPF and have no idea how properly implement formatting (for example, to present NavigateUri or Name only on UI) and implement generic handler for click on any element. Something like this:
private void Hyperlink_OnClick(object sender, RoutedEventArgs e)
{
var clickedLink = (Hyperlink) sender;
System.Diagnostics.Process.Start(clickedLink.NavigateUri.ToString());
}
I tried DataTemplate, tried a lot of other variants, googled really a lot, but still have no clue how to do that. Could you please suggest any easy and elegant solution?
DataTemplate is your best bet, here's how you could implement it in your case.
<ListView Name="ListIssueLinks" Height="100">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink NavigateUri="{Binding}" RequestNavigate="Link_RequestNavigate">
<TextBlock Text="{Binding}" />
</Hyperlink>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Then in your code behind, I would simply bind directly to your issueLinks (no need to build up HyperLinks in code).
List<string> issueLinks = new List<string>()
{
"http://www.google.com",
"http://www.stackoverflow.com",
};
ListIssueLinks.ItemsSource = issueLinks;
Then your RequestNavigate event handler needs to start your process
private void Link_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
I didnt fully understand what is your problem but i have two things that you should know that exist.
ObservableCollection
i think you should consider use it instead of a simple List because it has advantages if you need to bind the List to The View with your ListView for example. for more info read here: ObservableCollection Class
XAML side
the second thing i think you tried to explain is how to take properties and show them in your list view. this means you need to write it on the XAML side and explore about it farther, you can bind the properties after that with inotifypropertychanged if the data in the list is going to change with any reason.
hope i helped.

Data binding not giving expected results windows phone

In my windows phone app I've implemented data binding which is not yielding me expected results.
My functionality is I've a list box in which I've two textboxes which are data bound.
When I click the textbox datepicker/timepicker will open and the selected value should reflect in the textbox.
The xaml code for the listbox data template is as follows
<TextBox Visibility="{Binding TBVisibility}" IsReadOnly="{Binding TBReadOnly}" InputScope="{Binding Numeric}" AcceptsReturn="{Binding MultiLine}" Width="{Binding TBWidth}" TextWrapping="Wrap" Text="{Binding Path=TBText, Mode=TwoWay}" GotFocus="TextBox_GotFocus_1" KeyUp="TextBox_KeyUp_1" LostFocus="TextBox_LostFocus_1" />
<TextBox Visibility="{Binding TB2Visibility}" IsReadOnly="True" Width="140" Text="{Binding TB2Text, Mode=TwoWay}" GotFocus="TextBox_GotFocus_2" />
I'm launching the datepicker and timepicker as follows
private void LaunchDatePicker(TFDetails field)
{
datePicker = new CustomDatePicker
{
IsTabStop = false,
MaxHeight = 0,
Value = field.SelectedDate
};
datePicker.DataContext = field;
datePicker.ValueChanged += DatePicker_ValueChanged;
LayoutRoot.Children.Add(datePicker);
datePicker.ClickDateTemplateButton();
}
Where as "field" is the datacontext of the listbox.
The ValueChanged events are as follows
private void DatePicker_ValueChanged(object sender, DateTimeValueChangedEventArgs e)
{
DatePicker currentDP = sender as DatePicker;
TFDetails callingField = currentDP.DataContext as TFDetails;
if (callingField != null)
{
callingField.SelectedDate = currentDP.Value;
callingField.TBText = currentDP.ValueString;
}
}
When I change the time its not reflecting in the textbox. I wrote INotifyChangedProperty also.
May I know what mistake I could possible be doing here.
I actually have the same code in a similar UI page where it works perfectly. i don't know what mistake I'm doing here.
Thanks.
ListBox is a collection control. If you have a DataTemplate for it, bindings in it will use a single elements of collection boud to ItemsSource as a DataContext rather than DataContext od entire ListBox.

c# How to get Listbox selection from Observable Collection

I'm probably not even asking this correctly, I am new to c#, but trying to help my 14 year-old son learn. I've created a listbox with items created with an ObservableCollection. Here is the XAML:
<ListBox x:Name="listBox1" ItemsSource="{Binding}" Margin="105,205,886,63"
IsTabStop="True" SelectionChanged="PrintText"
ScrollViewer.VerticalScrollBarVisibility="Hidden" TabIndex="5" FontSize="36"
Background="Transparent" Foreground="#FF55B64C" FontFamily="Arabic Typesetting"
FontWeight="Bold" IsDoubleTapEnabled="False" SelectionMode="Single" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="blockNameList" Text="{Binding name}"/>
<TextBlock Text=" #"/>
<TextBlock Name="blockIdList" Text="{Binding id}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is how I created the ListBox Items:
var client = new HttpClient();
var uri = new Uri("http://theurlImusing");
Stream respStream2 = await client.GetStreamAsync(uri);
// DataContractJsonSerializer ser2 = new DataContractJsonSerializer(typeof(RootObject));
// RootObject feed2 = (RootObject)ser2.ReadObject(respStream2);
DataContractJsonSerializer ser = null;
ser = new DataContractJsonSerializer(typeof(ObservableCollection<RootObject>));
ObservableCollection<RootObject> feed2 = ser.ReadObject(respStream2) as ObservableCollection<RootObject>;
var cardList = new List<RootObject>();
foreach (RootObject returnfeed in feed2)
{
string cid = returnfeed.id;
string cardname = returnfeed.name;
listBox1.Items.Add(new RootObject { id=cid, name=cardname });
}
I thought I would just use the SelectionChanged="PrintText" property of the listbox so that when I clicked on a listbox item, it would just change a textblock's text value. Ultimately, that is all I am trying to do...set a textblock or textbox to be equal to the "id" value that is clicked on in the ListBox.
void PrintText(object sender, SelectionChangedEventArgs args)
{
//What do I put in here??
}
Thanks very much for any insight! I need it!!
This is something that is much easier to do using data binding. You can bind the TextBlock.Text property directly to the ListBox using an ElementName binding:
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedItem.id}" />
Alternatively, if you set set SelectedValuePath="id" on the ListBox, then binding to SelectedValue will give you the "id" property:
<ListBox x:Name="listBox1" SelectedValuePath="id" ... />
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedValue}" />
As a side note (as #Rachel already noted in comments): you may as well just set the ItemsSource, rather than looping through and adding each manually. All you need is this:
listBox1.ItemsSource = feed2;
Edit
Ok, if you wanted to use the procedural approach, here's how you would do it. (No one would recommend this approach, especially if you're learning/teaching. Try to make full use of data binding, and view-viewmodel separation.)
void PrintText(object sender, SelectionChangedEventArgs args)
{
var listBox = (ListBox)sender;
RootObject selectedItem = listBox.SelectedItem;
someTextBox.Text = selectedItem.id;
}
If all you want to do is click an item in the ListBox and get it to show up in the TextBox, you don't need fancy binding (in that other answer) to do it. You can simply add a MouseUp event in the ListBox XAML:
MouseUp="ListBox1_MouseUp"
This would work similar to the SelectionChanged event you wanted to use.
You then right-click that function name in the XAML page and select "Go to definition". It will create the next function for you:
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
}
Simply add in there to update the TextBox you want with the SelectedItem values from sender:
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
ListBox lstBox = (ListBox)sender;
ListBoxItem item = lstBox.SelectedItem;
if (item != null) // avoids exception when an empty line is clicked
{
someBox.Text = item.name;
someOtherBox.Text = item.id;
}
}
I later found that blockNameList and blockIdList are not accessible via intellisense because they are within the DataTemplate of the ListBox, so I put someBox and someOtherBox, as references to other TextBoxes you would have to add to the XAML, outside of the ListBox. You would not re-write data inside the ListBox on the same item by clicking it. Even if you could reach the template's TextBlock to do it, you'd just be re-writing that same item with its own values, since it would be the SelectedItem!
Even though there are those that don't recommend this approach because they like binding everything - and in some cases you want binding to occur so that controls on the page update as a result of dependencies (i.e. do one thing to cause another), I find that manual methods of clicking a button/item/control to update something are just fine and avoid all the model/MVVM BS that has taken over WPF and over-complicated it.

Handling Navigation in MVVM WPF application

I am developing a WPF application that follows MVVM. Now I am handling navigation of views in the following manner.
MainWindow View
<Border>
<StackPanel>
<local:Home
Content="{Binding CurrentView,Converter={StaticResource ViewConverterHome}, UpdateSourceTrigger=PropertyChanged}"/>
<local:Page1
Content="{Binding CurrentView,Converter={StaticResource ViewConverterPage1}, UpdateSourceTrigger=PropertyChanged}"/>
<local:Page2
Content="{Binding CurrentView,Converter={StaticResource ViewConverterPage2}, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Border>
Home, Page1,Page2 are 3 views. HomeVM,Page1VM,Page2VM are view models corresponding to the views. There is a class call ApplicationViewModel that contains a property CurrentView of type CViewModelBase which is the parent class for all three viewmodels. ApplicationViewModel handles the navigation in the folowing manner
private void OnUserInputNextClicked(object sender, OperationInformationChangedEventArgs e)
{
do
{
if (this.CurrentView is HomeVM)
{
this.CurrentView = null;
Page1VM page1 = new Page1VM("BNM", "MATH HONS", "13");
page1.NextCilcked += new EventHandler<OperationInformationChangedEventArgs>(OnUserInputNextClicked);
page1.BackCilcked += new EventHandler<OperationInformationChangedEventArgs>(OnUserInputBackClicked);
this.CurrentView = page1;
break;
}
if (this.CurrentView is Page1VM)
{
this.CurrentView = null;
Page2VM page2 = new Page2VM("Kolkata", "Monoj", "Itachuna");
page2.NextCilcked += new EventHandler<OperationInformationChangedEventArgs>(OnUserInputNextClicked);
page2.BackCilcked += new EventHandler<OperationInformationChangedEventArgs>(OnUserInputBackClicked);
this.CurrentView = page2;
break;
}
if (this.CurrentView is Page2VM)
{
this.CurrentView = null;
HomeVM home = new HomeVM("Anirban", "30");
home.NextCilcked += new EventHandler<OperationInformationChangedEventArgs>(OnUserInputNextClicked);
this.CurrentView = home;
break;
}
} while (false);
}
The navigation is working perfectly; But dispose of disappeared views are not getting called.So all the views live till the end. Is there any way to prevent that?
Your Views will always exist because you added a copy of each one to your UI with the XAML, even if the Content contained in them may not exist
Typically I will use a ContentControl to display content instead of creating an instance of the control for each content type, and I'll use DataTemplates to tell WPF how to draw each type of content.
For example,
<Window.Resources>
<DataTemplate DataType="{x:Type local:HomeVM}">
<local:Home Content="{Binding }" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:Page1VM}">
<local:Page1 Content="{Binding }" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:Page2VM}">
<local:Page2 Content="{Binding }" />
</DataTemplate>
</Window.Resources>
<Border>
<StackPanel>
<ContentControl Content="{Binding CurrentView}" />
</StackPanel>
</Border>
This way, you only have one instance of your Content in the VisualTree, and the DataTemplate WPF users to draw your content changes based on it's DataType.
I have an example of this kind of navigation with WPF on my blog if you're interested in checking out a full code sample
You need to change DataContext of MainWindow. It depends on your integration. When I make a MVVM application what I do is that pass MainWindow object to every view constructor. And whenever I have to move to next page (like on next button) I change the MainWindow object DataContext to new view.
Something like this.
public PageOneViewModel
{
private MainWindow _mainWindow;
public PageOneViewModel(MainWindow mainWindow)
{
// Here I am saving MainWindow object.
_mainWindow = mainWindow;
}
public OnNext()
{
// Here I am changing the view.
MainWindow.DataContext = new PageTwoViewModel(_mainWindow);
}
}
Have you considered using Frame?
<Frame Name="YourFrame" Navigated="OnNavigated"/>
and then you can call
YourFrame.CanGoBack(), YourFrame.GoBack()
etc.
Here's a link to my answer to a similar question with working source code. The technique I used is a little similar to Faisal's solution.
If you need a good downloadable sample solution that demonstrates navigation using a side menu, look at here and here(simpler example).

Categories