I've got this function:
void Title(string t)
{
if (called == true)
{
GrdLogo.Children.Remove((TextBlock)GrdLogo.FindName("Tb"));
}
TextBlock Tb = new TextBlock();
Tb.Text = t;
Tb.Name = "Tb";
Tb.Height = 160;
Tb.FontSize = 70;
Tb.Margin = new Thickness(Img.Width * 2 + 30, 30, 0, 0);
GrdLogo.Children.Add(Tb);
}
And after calling this function I get error:
WinRT information: The name already exists in the tree: Tb.
Xaml code of thisGrid (GrdLogo):
<Grid x:Name="GrdLogo" HorizontalAlignment="Left" Height="160" VerticalAlignment="Top" Width="1366" Background="#FF1D1D1D">
<Grid.ChildrenTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</Grid.ChildrenTransitions>
</Grid>
Additionally, without transistion it works but I don't know how it can make any difference so I'm asking for help. I'm still pretty fresh in creating dynamic controls. Anyway for me code looks good but maybe I should use some kind of async method to delete this TextBlock?
The error appears because GrdLogo instance doesn't know you removed element named Bd from it's visual tree. You need to notify it by calling FrameworkElement.UnregisterName:
if (called == true)
{
GrdLogo.Children.Remove((TextBlock)GrdLogo.FindName("Tb"));
GrdLogo.UnregisterName("Tb");
}
EDIT
Since you cannot use that method in your W8 code (there is no namescope access in WinRT), you should avoid using named elements altogether. Remove your textblock name in xaml and use other means of removing control like:
GrdLogo.Children.Remove(GrdLogo.Children.OfType<TextBlock>().Single());
Related
This question already has answers here:
Append a child to a grid, set it's row and column
(4 answers)
Closed 1 year ago.
I am learning algorithmics and C#, and I wanted to write a sudoku solver with some WPF interface.
I need 81 textboxes (one for each sudoku box) and I wanted to generate them with code and put them in array, instead of having 81 different objects.
I used the Visual Studio toolbox to drag and drop buttons in my main window. I also drag and dropped TextBoxes to get an idea how they would look like.
I don't find how to add the TextBoxes to the main Window, using a loop.
Here is the MainWindow.x.aml code :
<Window x:Class="SudokuPOO.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:SudokuPOO"
mc:Ignorable="d"
Title="Solveur de Sudoku" Height="260" Width="210">
<Grid>
<!--
<TextBox x:Name="T0" HorizontalAlignment="Left" Margin=" 10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="20"/>
<TextBox x:Name="T1" HorizontalAlignment="Left" Margin=" 30,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="20"/>
[ I DELETED STUFF HERE ]
-->
<Button Content="Résoudre" HorizontalAlignment="Left" Margin="10,210,0,0" VerticalAlignment="Top" Click="Resoudre"/>
<Button Content="Effacer tout" HorizontalAlignment="Left" Margin="75,210,0,0" VerticalAlignment="Top" Click="Effacer"/>
</Grid>
Here is the MainWindow.x.aml.cs I am trying to edit :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); // Was already here.
GenerateTextBoxes(); // I added that manually.
}
public void GenerateTextBoxes()
{
TextBox[] TextBoxes = new TextBox[5];
for (int i = 0; i<=4; i++)
{
TextBox TBox = new TextBox();
TBox.Margin = new Thickness(10+20*i, 10, 0, 0);
TBox.Text = i.ToString();
TBox.Width = 20;
TBox.Height = 20;
// this.Controls.Add(TBox); // Doesn't work.
this.Content = TBox; // Trouble here.
TextBoxes[i] = TBox;
}
}
}
I tried with only 5 textboxes to understand what is happening.
This code runs, it displays the MainWindow, but with only one TextBox with "4" in it.
My guess is that
this.Content = Tbox;
replaces all content of the window with only one TBox.
How do I a just add the Tbox ?
The XAML files suggests that a "TextBox" is inside a "Grid" that is inside the "MainWindow", but "this.Grid" doesn't exist, neither "MainWindow.Grid".
I tried stuff like "LayoutRoot.Children.Add", "this.Controls.Add", "this.Grid.Add" and so on, nothing works. Almost all the code I can find online related to TextBox in C# uses "Windows Forms" and not "WPF" and doesn't work in my case (like how to define an array of textboxes in c#? ).
Give the root panel in the XAML markup a name:
<Grid x:Name="grid">
...and then add the textboxes to the panel's Children collection:
grid.Children.Add(TBox);
Also note that that there might be better panels to use for layout instead of mocking around with margins: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/panels-overview?view=netframeworkdesktop-4.8
I want to create a page like Android playstore in which i have to create multiple horizontal scrollable Gridviews on the basis of data at runtime. As i am new to windows phone development i don't know how to create it dynamically. So Please provide any type of help or tutorial regarding this.
I have implemented the below code with this code i am able to produce the required result but the gridview items are not stacked horizontally.I want to make the items scroll horizontally So please provide any help with which required result can be achieved.I am attaching a screenshot for reference.
public void DesignUi()
{
GridViewItem grdItem = new GridViewItem();
for (int i = 0; i < 20; i++)
{
string backgroundColor = string.Empty;
StackPanel staParent = new StackPanel();
#region Header
StackPanel headerStack = new StackPanel();
headerStack.Background = new SolidColorBrush(Colors.Pink);
TextBlock textHeader = new TextBlock();
textHeader.Text = "Header :-" + i;
headerStack.Children.Add(textHeader);
#endregion
#region Body
StackPanel staBody = new StackPanel();
staBody.Background = new SolidColorBrush(Colors.Green);
#region Create Grid View
GridView grd = new GridView();
grd.SetValue(ScrollViewer.VerticalScrollModeProperty, ScrollMode.Disabled);
grd.SetValue(ScrollViewer.HorizontalScrollModeProperty, ScrollMode.Enabled);
ItemsPanelTemplate itmPanel = new ItemsPanelTemplate();
VirtualizingStackPanel vrStack = new VirtualizingStackPanel();
vrStack.Orientation = Orientation.Horizontal;
TextBlock textQ = new TextBlock();
textQ.Text = "";
vrStack.Children.Add(textQ);
itmPanel.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, true);
itmPanel.SetValue(VirtualizingStackPanel.OrientationProperty, Orientation.Horizontal);
itmPanel.SetValue(ItemsControl.ItemContainerStyleProperty, Orientation.Horizontal);
ItemsControl itmCntrl = new ItemsControl();
itmCntrl.Items.Add(vrStack);
#region Create Gridview Items
for (int j = 0; j < 4; j++)
{
grdItem = new GridViewItem();
grdItem.Width = 100;
grdItem.Height = 150;
grdItem.Margin = new Thickness(5, 5, 5, 5);
grdItem.Background = new SolidColorBrush(Colors.Red);
TextBlock textGrd = new TextBlock();
textGrd.Text = "Item :-" + j;
grdItem.Content = textGrd;
grd.Items.Add(grdItem);
}
#endregion
#endregion
staBody.Children.Add(grd);
#endregion
staParent.Children.Add(headerStack);
staParent.Children.Add(staBody);
staLists.Children.Add(staParent);
}
}
Current Result Screenshot with the above code:---
Required Result Screenshot
I had to put this as an answer because the comment section was too limiting for this. So I better answer this and guide you towards the correct approach
OKay! there are better ways to do this. The best way and the easiest is via DataBinding. It'll reduce your code to almost nothing, and it'll be easier for you to design your GridView in XAML rather than doing it via c#. if you are not familiar with the concept of data binding and you want to implement it the way you're doing it now then I'll add to your solution that, the GridView would stack horizontally by setting the ItemsWrapGrid.Orientation property of your gridview to vertical to stack your elements horizontally and remember to set the scroll mode to horizontal too.
For the scroll mode: add the below to your GridView XAML
ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.VerticalScrollMode="Disabled"
For setting the ItemsWrapGrid Orientation Property:
string template =
"<ItemsPanelTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"><ItemsWrapGrid VerticalAlignment = \"Top\" "
+ " ItemWidth = \""
+ itemWidth
+ "\" Orientation = \"Vertical\"/></ItemsPanelTemplate> ";
yourgridview.ItemsPanel = (ItemsPanelTemplate)XamlReader.Load(template);
Please Note:
The Better and cleaner way to achieve this would be via DataBinding, The Below is the code for achieving this via DataBinding:
The XAML
<GridView Name="ViewView" HorizontalAlignment="Center" ItemTemplate="{StaticResource AllAppsTileData}" IsItemClickEnabled="True" SelectionMode="Single" Margin="10" ItemsSource="{Binding AppsToShow}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.VerticalScrollMode="Disabled">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Vertical"/> <--Change this to Horizontal for vertically wrapping the items-->
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="5"/>
</Style>
</GridView.ItemContainerStyle>
</GridView>
The DataTemplate
To be defined in your <Page.Resources>
<DataTemplate x:Key="AllAppsTileData">
<Grid>
<Grid.Background>
<ImageBrush Stretch="Fill" ImageSource="{Binding AppImage}"/>
</Grid.Background>
<Grid>
<Grid.Background>
<SolidColorBrush Color="Black" Opacity="0.3"/>
</Grid.Background>
<TextBlock Text="{Binding AppName}" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
</DataTemplate>
Your backing app class
public class AppDataClass
{
public string AppName { get; set; }
public string AppImage { get; set; } //The image would be something like ms-appx:///Assets/LOGO.png
}
Now that you have your architecture ready, there are two ways you can go about it from here,
You bind the ItemsSource property of the GridView to an ObservableCollection<AppDataClass> which can be populated by your code behind or preferably a ViewModel using the MVVM approach and each time the ObservableCollection<AppDataClass> changes, it raises the RasiePropertyChanged event from the interface INotifyPropertyChanged and the view automatically updates itself. This is a highly recommended approach as it keeps your UI and Business Logic on two different threads and either one of them would not interact with each other, they'll get the data via the ViewModel, this is the MVVM approach for more information on it use This article
As you explained that you're new to the Phone development, I would say forget all about the 1st point because it can be tough to grasp if you're new to the platform, what i'll recommend is the easy way,
From your code behind get the data into a List something like this,
List<AppDataClass> MyEntireData = new List<AppDataClass>();
MyEntireData = GetData();
where the GetData method is returning you a List<AppDataClass> and now simply after the MyEntireData is not empty or it's count is > 0 use, ViewView.ItemsSource = MyEntireData;
And you'll have a much more organized code which provides you the store way kinda layout.
And in future if you want to change the way the Tiles look you don't need to wrap your head to the c# generated XAML, you just need to modify the DataTemplate.
If there is anything do let me know in the comments section.
I have a UserControl that I instantiate in code behind and would like to print. When I print this UserControl the code prints a blank piece of paper. Why is this? My code is as follows
private void PrintCurrentTab(object sender, RoutedEventArgs e)
{
PrintDialog printDlg = new PrintDialog();
var child = MyMainWindowViewModel.SelectedTab.Content;
if (child is ScrollViewer)
{
child = (((ScrollViewer)child).Content);
}
if (printDlg.ShowDialog() == true)
{
var printControl = new PrintingTemplate();
printDlg.PrintVisual(printControl, "User Control Printing.");
}
}
My UserControl is as follows
<UserControl x:Class="MyApp.Views.PrintingTemplate"
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"
mc:Ignorable="d"
MinHeight="500"
MaxHeight="1000"
MinWidth="200"
MaxWidth="1000"
Height="1056"
Width="816">
<StackPanel>
<Grid>
<Image Source="..\Resources\Images\PrintLogo.jpg" Width="150" HorizontalAlignment="Right" Margin="20"/>
<Rectangle Fill="Black" Margin="10,40,150,0" Height="2"/>
</Grid>
<Grid Name="PrintingGrid"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Label Content="Printed By:"/>
<Label Name="PrintedBy"/>
<Label Content="Printed On:"/>
<Label Name="PrintedDate"/>
</StackPanel>
</StackPanel>
</UserControl>
I got a similar problem where I was able to print from some computers but not from one (only blank page), on a physical printer (working OK with XPS). I finally got a working solution here:
» https://social.msdn.microsoft.com/Forums/vstudio/en-US/9eb79e11-ee5a-4687-ad4c-a6d96276a8f4/printing-a-wpf-usercontrol?forum=wpf
UserControlToPrint.Measure(New Size(816, 1056))
UserControlToPrint.Arrange(New Rect(New Size(816, 1056)))
UserControlToPrint.UpdateLayout()
Regards,
François from Québec
perhaps the new in the following code is the culprit
if (printDlg.ShowDialog() == true)
{
var printControl = new PrintingTemplate();
printDlg.PrintVisual(printControl, "User Control Printing.");
}
try to get the handle to the current control rather than create a new
Have you debugged this at all? Your question seems incomplete, and I don't really know what you're trying to accomplish. I DO notice that an important step is missing. Here are some questions I have though:
Is MyMainWindowViewModel.SelectedTab.Content == null?
What is child's type?
What is the purpose of child = (((ScrollViewer)child).Content);--that's ugly
Actually why is any of the child code in there, its not being used!
Anyways, you can't print a control that hasn't gone through a layout pass. So now you might ask, "how do I force a control to be rendered?" Simple, like this:
if (printDlg.ShowDialog() == true)
{
var printableArea = new Size(printDlg.PrintableAreaWidth, printDlg.PrintableAreaHeight)
var printControl = new PrintingTemplate();
//Set the drawing dimensions/boundaries - notice (Acutal)Width/Height = 0
printControl.Measure(printableArea);
printControl.Arrange(new Rect(new Point(), printableArea);
//"Render"!
printcontrol.UpdateLayout();
//At this point you should see the (Acutal)Width/Height be > 0!
printDlg.PrintVisual(printControl, "User Control Printing.");
}
Oxyplot graphs 13 points which are derived from the 6 user input text boxes. The values in the text boxes are held in public variables in the MainWindow.xaml.cs class. The variables are updated when the user presses enter in the text box. How would I make the refresh button refresh the graph.
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
//Refresh The Graph
}
I think that this would be done using the
PlotModel.RefreshPlot()
method, but I am not sure how to implement it because of Oxyplot's poor documentation.
I just updated to a new version of OxyPlot via NuGet. I'm using OxyPlot.Wpf v20014.1.277.1 and I think you now need to call InvalidatePlot(bool updateData) on the PlotModel instead of RefreshPlot (which is no longer available). I tested this in my sample code and it worked as expected.
If you want to refresh the plot and update the data collections, you need to pass true to the call:
PlotModel.InvalidatePlot(true)
Give x:Name to OxyPlot instance in XAML:
<oxy:Plot x:Name="Plot1"/>
and on button click handler, refresh like this:
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
Plot1.RefreshPlot(true);
}
The cleanest way I've found to get "sort of" auto-update is reacting to CollectionChanged on the collection that is LineSeries' ItemsSource.
In ViewModel:
ObservableCollection<DataPoint> Data { get; set; }
= new ObservableCollection<DataPoint>();
public PlotModel PlotModel
{
get { return _plot_model; }
set
{
_plot_model = value;
RaisePropertyChanged(() => PlotModel);
}
}
PlotModel _plot_model;
// Inside constructor:
Data.CollectionChanged += (a, b) => PlotModel.InvalidatePlot(true);
In the current OxyPlot.Wpf (1.0.0-unstable1983) you have two options:
Bind the Series.ItemsSource property from XAML to a collection in your viewmodel and exchange the whole collection, when you need an update. This also allows for concurrent async updates with larger data sets.
Bind the Plot.InvalidateFlag property of type int to your viewmodel and increment whenever you need an update. I haven't tested this approach, though.
The following code illustrates both options (pick one). XAML:
<oxy:Plot InvalidateFlag="{Binding InvalidateFlag}">
<oxy:Plot.Series>
<oxy:LineSeries ItemsSource="{Binding DataSeries}" />
</oxy:Plot.Series>
</oxy:Plot>
Updates on the ViewModel:
private async Task UpdateAsync()
{
// TODO do some heavy computation here
List<DataPoint> data = await ...
// option 1: Trigger INotifyPropertyChanged on the ItemsSource.
// Concurrent access is ok here.
this.DataSeries = data; // switch data sets
// option 2: Update the data in place and trigger via flag
// Only one update at a time.
this.DataSeries.Clear();
data.ForEach(this.DataSeries.Add);
this.InvalidateFlag++;
}
After having the same question with the same issue, it would seem that the only working solution (at least to my point of view) is as followed :
PlotView.InvalidatePlot(true)
Doing so, after updating one or multple Series do refresh your PlotView.
The refresh rate depends on how often, or at which rate your serie(s) is/are updated.
Here is a code snippet (on Xamarin Android but should work anyway) :
PlotView resultsChart = FindViewById<PlotView>(Resource.Id.resultsChart);
PlotModel plotModel = new PlotModel
{
// set here main properties such as the legend, the title, etc. example :
Title = "My Awesome Real-Time Updated Chart",
TitleHorizontalAlignment = TitleHorizontalAlignment.CenteredWithinPlotArea,
LegendTitle = "I am a Legend",
LegendOrientation = LegendOrientation.Horizontal,
LegendPlacement = LegendPlacement.Inside,
LegendPosition = LegendPosition.TopRight
// there are many other properties you can set here
}
// now let's define X and Y axis for the plot model
LinearAxis xAxis = new LinearAxis();
xAxis.Position = AxisPosition.Bottom;
xAxis.Title = "Time (hours)";
LinearAxis yAxis = new LinearAxis();
yAxis.Position = AxisPosition.Left;
yAxis.Title = "Values";
plotModel.Axes.Add(xAxis);
plotModel.Axes.Add(yAxis);
// Finally let's define a LineSerie
LineSeries lineSerie = new LineSeries
{
StrokeThickness = 2,
CanTrackerInterpolatePoints = false,
Title = "Value",
Smooth = false
};
plotModel.Series.Add(lineSerie);
resultsChart.Model = plotModel;
Now, whenever you need to add DataPoints to your LineSerie and to updated automatically the PlotView accordingly, just do as followed :
resultsChart.InvalidatePlot(true);
Doing so will automatically refresh your PlotView.
On a side note, the PlotView will also be updated when an event occurs such as a touch, a pinch to zoom, or any kind of UI-related events.
I hope I could help. I had trouble with this for a very long time.
Exists three alternatives how refresh plot (from OxyPlot documentation):
Change the Model property of the PlotView control
Call Invalidate on the PlotView control
Call Invalidate on the PlotModel
Another two years later... this solution works for me, because I have no oxyplot models and I´m missing some of the named functions from above.
code behind:
public partial class LineChart : UserControl
{
public LineChart()
{
InitializeComponent();
DataContext = this;
myChart.Title = "hier könnte Ihr Text stehen!";
this.Points = new List<DataPoint>();
randomPoints();
}
public IList<DataPoint> Points { get; private set; }
public void randomPoints()
{
Random rd = new Random();
String myText = "";
int anz = rd.Next(30, 60);
for (int i = 0; i < anz; i++)
myText += i + "," + rd.Next(0, 99) + ";";
myText = myText.Substring(0, myText.Length - 1);
String[] splitText = myText.Split(';');
for (int i = 0; i < splitText.Length; i++)
{
String[] tmp = splitText[i].Split(',');
Points.Add(new DataPoint(Double.Parse(tmp[0].Trim()), Double.Parse(tmp[1].Trim())));
}
while (Points.Count > anz)
Points.RemoveAt(0);
myChart.InvalidatePlot(true);
}
}
To update your data don't exchange the whole IList, rather add some new DataPoints to it and remove old ones at position 0.
XAML:
<UserControl x:Class="UxHMI.LineChart"
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:UxHMI"
xmlns:oxy="http://oxyplot.org/wpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="Container" Background="White">
<oxy:Plot x:Name="myChart" Title="{Binding Title}" FontFamily="Bosch Sans Medium" Foreground="#FF0C6596" FontSize="19" Canvas.Left="298" Canvas.Top="32" Background="AliceBlue" Margin="0,0,10,0">
<oxy:Plot.Series>
<oxy:LineSeries x:Name="ls" Background="White" ItemsSource="{Binding Points}" LineStyle="Solid" Color="ForestGreen" MarkerType="None" MarkerSize="5" MarkerFill="Black">
</oxy:LineSeries>
</oxy:Plot.Series>
</oxy:Plot>
<Button x:Name="button" Content="Random" HorizontalAlignment="Left" Margin="0,278,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
</Grid>
important are the x:Name="myChart" and ItemsSource="{Binding Points}"
I hope this is useful for someone out there
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!