I am working in Xamarin but I believe this applies to any UWP application using XAML.
First I have two ContentPages. On the first page, I want to pass some data to the second page, so I do this as part of the navigation:
async void BuyTickets(object sender, EventArgs e)
{
var ticketOrderTotal = new TicketOrder
{
OrderTotal = lblOrderAmount.Text,
OrderTotalList = ticketsPrices.Where(o => o.TicketQuantity > 0).ToList<Ticket>()
};
var paymentPage = new PaymentPage();
paymentPage.BindingContext = ticketOrderTotal;
await Navigation.PushAsync(paymentPage);
}
The above works fine in XAML. On the second page (PaymentPage), I am able to reference the BindingContext like this, for example, and the Text property is correct:
<Label x:Name="lblOrderAmount" Text="{Binding OrderTotal}" />
What I would like to do is access the "{Binding OrderTotal}" value in the C# code-behind of the second page. I found a way to do this, too, but it just does not seem optimal. This is the kludge I have in place:
<Label x:Name="lblOrderAmount" Text="{Binding OrderTotal}" BindingContextChanged="GetChargeAmount" />
And this is the code-behind for the label:
public static string m_charge_amount = "";
...
private void GetChargeAmount(object sender, EventArgs e)
{
var lbl = ((Label)sender);
m_charge_amount = lbl.Text;
}
So my question is this: is there a better way to do this? It is particularly hard to research as XAML seems to be rooted in WPF, Silverlight, Xamarin, Windows 8, and now Windows 10 (UWP). It is all over the place. I am constantly fighting with the framework to do things that I think should be quite easy to do....like this. Please help but do be nice.
Thank you.
EDIT:
Per #Jason's comment, you can pass an object to the page constructor, and that will work. This is what the re-worked function looks like now:
async void BuyTickets(object sender, EventArgs e)
{
var ticketOrderTotal = new TicketOrder
{
OrderTotal = lblOrderAmount.Text,
OrderTotalList = ticketsPrices.Where(o => o.TicketQuantity > 0).ToList<Ticket>()
};
var paymentPage = new PaymentPage(ticketOrderTotal);
paymentPage.BindingContext = ticketOrderTotal;
await Navigation.PushAsync(paymentPage);
}
And then the result from debugging:
instead of having PageA set PageB's BindingContext, instead pass the ticketOrderTotal object as a parameter on PageB's constructor. Then PageB can set it's own BindingContext as well as keep a local reference to the ticketOrderTotal object.
Related
Hope you can help me whit this :)
I WANT TO: give the following values to four different radio buttons: 30, 50, 100 and 200 (doesn't really matter what the values is for now). Right now I need to go into the code and change the number my self. I want these radio buttons to do the job when checked.
I will paste the code here. Can you please be very spesific when explaining this to me (if you can and bother to do so).
Thank you!
//Method for establishing connection to database.
// Sette parameter for limit
public static MongoDatabase GetDatabase(string searchText)
{
/* try
{*/
TweetOC.Clear();
MongoServerSettings settings = new MongoServerSettings();
settings.Server = new MongoServerAddress("xxxx", xxxx);
MongoServer server = new MongoServer(settings);
MongoDatabase database = server.GetDatabase("tweet_database");
var collection = database.GetCollection<Tweets>("docs");
System.Console.WriteLine("5");
var query = Query.And(Query.Matches("text", searchText),
Query.NE("geo_enabled", false));
System.Console.WriteLine("6");
//var match = Query.ElemMatch("text", query);
var cursor = collection.Find(query);
cursor.SetLimit(30);
System.Console.WriteLine("7");
//Puts the result from the last query into a list.
var resultList = cursor.ToList();
//Iterates over the previous mentioned list and inserts the content into the ObservableCollcetion created earlier.
foreach (var item in resultList)
TweetOC.Add(item);
System.Console.WriteLine(TweetOC.Count);
return database;
}
I HAVE TO CHANGE CURSOR.LIMIT(manually) ALL THE TIME. I WANT THIS NUMBER TO CHANGE AUTOMATICALLY WHEN A RADIO BUTTON IS CHECKED.
THE METHOD AND XAML WILL FOLLOW:
// I want this to be if radiobutton is 20, then this should be sent to cursor.set limit. I cannot make another string in database.cs without getting an error.
/* Private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
var radioButton = sender as RadioButton;
if (radioButton == 20)
return;
int intIndex = Convert.ToInt32(radioButton.Content.ToString(Cursor.SetLimit));
}
* Remember Checked="RadioButton_Checked" in the XAML if you want to try
*/
XAML for one of the four buttons:
<RadioButton Content="RadioButton" Grid.Column="2" HorizontalAlignment="Left"
Margin="20,116,0,0" Grid.Row="2" VerticalAlignment="Top"/>
HOW SHOULD THIS ACTUALLY LOOK TO GET IT WORKING? PLEASE EDIT THE CODE (if you bother) SO I CAN SEE AND UNDERSTAND THIS.
THANKS AGAIN!
To do this in an MVVM friendly way, bind the IsChecked property like this:
IsChecked="{Binding Path=CursorLimit, Converter={StaticResource ParamToIntConverter}, ConverterParameter=10}"
Set the parameter to the correct value for the given radio button, of course. If you are not familiar with converters, you need a line in your resources like (assuming you have the local xmlns set up to point to the converter namespace):
<local:ParamToIntConverter x:Key="ParamToIntConverter"/>
Then your converter looks like:
public class ParamToIntConverter : IValueConverter
{
public object Convert (...)
{
return value.Equals(int.Parse((string)parameter));
}
public object ConvertBack(...)
{
if ((bool)value)
return int.Parse((string)parameter);
else
return Binding.DoNothing;
}
}
Your xmal code:
<RadioButton Content="RadioButton" Grid.Column="2" HorizontalAlignment="Left"
Margin="20,116,0,0" Grid.Row="2" VerticalAlignment="Top" Checked="RadioButton_Checked" />
Your *.cs code:
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
// ... do what you need for this button
}
Use different Checked="[insert_method_name]" template with equal method names in your *.cs file and do what you need in each method.
Also you can try to make it as in this http://www.dotnetperls.com/radiobutton-wpf example.
I'm new to WP8 and follow many tutorials. For parts of the menu I use a viewModel with NotifyPropertyChanged. When I get my list of news articles it creates a viewModel and displays it in a longListSelector.
But also I want to make 1 HubTile with the image and some preview-text of the first article. Is there a nice way to send some event to the .xaml.cs? Or do I have to make another viewModel for this one HubTile and make a binding?
Ony try was to make such a variable:
private bool _isDataLoaded = false;
public bool IsDataLoaded
{
get
{
return _isDataLoaded;
}
set
{
if (value != _isDataLoaded)
{
_isDataLoaded = value;
NotifyPropertyChanged("IsDataLoaded");
}
}
}
The same thing is used with "IsLoading"-variable to create a loading-indicator in the systemTray:
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("MainPage_Loaded-Funktion");
Binding binding = new Binding("IsLoading") { Source = DataContext };
BindingOperations.SetBinding(
prog, ProgressIndicator.IsVisibleProperty, binding);
binding = new Binding("IsLoading") { Source = DataContext };
BindingOperations.SetBinding(
prog, ProgressIndicator.IsIndeterminateProperty, binding);
prog.Text = "Lade aktuelle Inhalte...";
}
Can I use this to call a function, when my variable is set and I get a notification?
The solution that helped me out was this:
<toolkit:HubTile Message="{Binding OnlineNews[0].TeaserText}"/>
Didn't know that you can access the viewModel like that. Thanks to Toni Petrina!
I am attempting to use MVVM to Bind a Windows Form Control to a panel in WPF. My overall objective is to be able to dynamically change which specific Windows Form Control I will use as I plan on having potentially several available.
Right now, I have been able to get this to work by having the application launch a callback on initialization which accesses the grid object by name. Here is how XAML currently looks:
<Grid Name="WindowsControlObject"></Grid>
The Callback looks like the following:
private void WindowLoaded(object sender, RoutedEventArgs e)
{
System.Windows.Forms.Integration.WindowsFormsHost host =
new System.Windows.Forms.Integration.WindowsFormsHost();
System.Windows.Forms.Control activeXControl = new SomeWindowsControl();
host.Child = activeXControl;
this.WindowsControlObject.Children.Add(host);
}
While this works, I am trying to fully utilize the MVVM pattern, as such is there a way I can do something like the following in the XAML/ModelView:
XAML:
<Grid Content="{Binding WindowsControl"></Grid>
In my ModelView:
public class MyModelView
{
public Grid WindowsControl;
public MyModelView{
WindowsControl = new Grid;
System.Windows.Forms.Integration.WindowsFormsHost host =
new System.Windows.Forms.Integration.WindowsFormsHost();
System.Windows.Forms.Control activeXControl = new SomeWindowsControl();
host.Child = activeXControl;
WindowsControl.WindowsControlObject.Children.Add(host);
}
}
Am I even right in my exploration/possible approach? It has occurred to me that I might need to use some other type of panel (other than grid), but haven't found anything obvious yet. If it can't be done, I have a solution, just not a very clean one.
Doing more digging, it turns out that I really wanted to bind this to a "ContentControl" tag, as follows:
XAML:
<ContentControl Content="{Binding WindowsControl}"/>
ViewModel:
private System.Windows.Forms.Control _myControl;
public WindowsFormsHost STKObject
{
get
{
return new WindowsFormsHost() { Child = _myControl};
}
}
I have the same problem like this. But I´m using a DataGrid instead of a ListBox and it does not seem to work like this (it might also be because i never used visual basic and didnt translate the code correcly into c#).
I basicly want two DataGrids on the same data with different filters.
ICollectionView view_dataLinesUnfiltered;
ICollectionView view_dataLinesFiltered;
public MainWindow()
{
...
//view_dataLines = CollectionViewSource.GetDefaultView(dataLines); // <- Filter works on both
view_dataLinesUnfiltered = new CollectionView(dataLines); // <- Filter doesn´t work at all
view_dataLinesFiltered = new CollectionView(dataLines);
....
// Control Events
this.ShowAA.RaiseEvent(new RoutedEventArgs(System.Windows.Controls.Primitives.ToggleButton.UncheckedEvent));
}
private void ShowAA_Checked(object sender, RoutedEventArgs e)
{
view_dataLinesUnfiltered.Filter = null;
}
private void ShowAA_UnChecked(object sender, RoutedEventArgs e)
{
view_dataLinesUnfiltered.Filter = delegate(object o) { return FilterContent(o as ErrorDetection.stDataLine, "AA", ""); };
}
bool FilterContent(ErrorDetection.stDataLine line, string sFilterAA, string sFilter)
{
shortArrayToHexStringConverter converter = new shortArrayToHexStringConverter();
string comBuffer = converter.Convert(line.ComBufferP as object,typeof(string),0,System.Globalization.CultureInfo.CurrentCulture) as string;
return false;// !comBuffer.Contains("AA");
}
The FilterContent method is being called without problems, but the DataGrid shows the lines anyway. If I use GetDefaultView the Filter works on both Datagrids. Do I have to use some other view instead of CollectionView (ListCollectionView does also not work)?
i have made a small sample project to show the problem sample. It only consists of an constructor and an observable collection.
I got it to work somehow. I used CollectionViewSources now instead of ICollectionView.
<Window.Resources>
<CollectionViewSource x:Key="viewSource_dataLinesUnfiltered"/>
<CollectionViewSource x:Key="viewSource_dataLinesFiltered"/>
</Window.Resources>
...
<DataGrid Name="Filtered_Datagrid" ItemsSource="{Binding Source={StaticResource viewSource_dataLinesFiltered}}" >
...
</DataGrid>
...
<DataGrid Name="Unfiltered_Datagrid" ItemsSource="{Binding Source={StaticResource viewSource_dataLinesUnfiltered}}">
...
</DataGrid>
and the c Code:
CollectionViewSource viewSource_dataLinesUnfiltered;
CollectionViewSource viewSource_dataLinesFiltered;
...
public MainWindow()
{
InitializeComponent();
this.DataContext = dataLines;
viewSource_dataLinesUnfiltered = (CollectionViewSource)this.Resources["viewSource_dataLinesUnfiltered"];
viewSource_dataLinesUnfiltered.Source = dataLines;
viewSource_dataLinesFiltered = (CollectionViewSource)this.Resources["viewSource_dataLinesFiltered"];
viewSource_dataLinesFiltered.Source = dataLines;
// Control Events
this.ShowAA.RaiseEvent(new RoutedEventArgs(System.Windows.Controls.Primitives.ToggleButton.UncheckedEvent));
}
private void ShowAA_Checked(object sender, RoutedEventArgs e)
{
viewSource_dataLinesUnfiltered.View.Filter = null;
}
private void ShowAA_UnChecked(object sender, RoutedEventArgs e)
{
viewSource_dataLinesUnfiltered.View.Filter = delegate(object o) { return FilterContent(o as ErrorDetection.stDataLine, "AA", ""); };
}
bool FilterContent(ErrorDetection.stDataLine line, string sFilterAA, string sFilter)
{
shortArrayToHexStringConverter converter = new shortArrayToHexStringConverter();
string comBuffer = converter.Convert(line.ComBufferP as object,typeof(string),0,System.Globalization.CultureInfo.CurrentCulture) as string;
return !comBuffer.Contains("AA");
}
But I´m not sure why it works this way and the filter is not applied on window repaints when ICollectionView is used.
You need to specify which ICollectionVIew is used on which DataGrid.
If you just bind to the collection (dataLines in this case) WPF will use the 'default view' (or create one if necessary), this is why the first commented out line works for filtering.
There are a few ways you could specify which view is used for which datagrid, depending on what patterns, etc. you are using
1) Like the linked question, you could set the ItemsSource for each DataGrid in the window's code behind, after initializing the views, e.g.:
filteredDataGrid.ItemsSource = view_dataLinesFiltered;
unfilteredDataGrid.ItemsSource = view_dataLinesUnfiltered;
2) You could set the DataContext of the window to itself, or make a view model for the screen that contains the view, and make the views public properties and then bind to the intended view for each grid, e.g.
<DataGrid ItemsSource="{Binding View_dataLinesFiltered}"> ....
Edit:
Now I'm not at work and can get to dropbox and play with your example it seems like the cause of the weird behaviour is the use of CollectionView directly. On the msdn page for CollectionView it says
You should not create objects of this class in your code. To create a
collection view for a collection that only implements IEnumerable,
create a CollectionViewSource object, add your collection to the
Source property, and get the collection view from the View property.
However, if you don't want to set up the views in XAML, you could also change your CollectionViews to ListCollectionViews and it should work as expected (this is likely the view type that CollectionViewSource is making for you behind the scenes anyway).
In silverlight 3 I am doing something like :
//currentDataForm.itemssource = currentCollisionDisplay;
//<input:AutoCompleteBox Width="74" x:Name="InvolvedCnt" Text="{Binding involvedCnt, Mode=TwoWay}"
...
for (int i = 0; i < driverNums; i++)
{
AddCollisionVehicle_Click(null, null);
}
...
private void AddCollisionVehicle_Click(object sender, RoutedEventArgs e)
{
currentCollisionDisplay.involvedCnt ++;
(df_collision.FindNameInContent("InvolvedCnt") as AutoCompleteBox).Text = currentCollisionDisplay.involvedCnt.ToString();
(df_collision.FindNameInContent("InvolvedCnt") as AutoCompleteBox).UpdateLayout();
string testString = (df_collision.FindNameInContent("InvolvedCnt") as AutoCompleteBox).Text;
}
so the initial value of the autocompletebox is "1".
if driverNums = 1 then the autocompletebox.text is 2.. which is correct
but if driverNums = 2 then the autocompletebox.text is 2.. which is wrong.
I changed the autocompletebox text field within an array, but this is not updated properly.. does anybody know how to fix this issue?
Strange thing is if i check testString variable, the value is correct..
This appears to be a known issue. See http://forums.silverlight.net/forums/p/199616/519232.aspx
A workaround that worked in my application is to clear out the Text property and then set it again using Dispatcher.BeginInvoke, i.e. something like this:
autoCompleteBox.Text = null;
Dispatcher.BeginInvoke(() =>
autoCompleteBox.Text = currentCollisionDisplay.involvedCnt.ToString());
I've only done this in one scenario so I don't know for sure if it'll work for you. My application also used bindings (I called ClearValue and then SetBinding).
You need to use the method "ValueMemberBrinding"
<toolkit:AutoCompleteBox x:Name="InvolvedCnt" ValueMemberBinding="{Binding Name}"/>