how to reference the object inside a UIControl in xaml - c#

So at first I create a uicontrol A ,In MainWindow.xaml.cs, I can click a button to create a new A and then I use A.DataContext = new Book(....). Also, in the Uicontrol, i can click a button to call the following method
private void OnShowBook(object sender, RoutedEventArgs e)
{
Book theBook = this.DataContext as Book;
if (theBook != null)
MessageBox.Show(theBook.Title, theBook.Isbn);
}
This works because I created a new Book using A.DataContent =... before.
Now I changed a little bit. i did not create an instance using A.DataContent = new Book(...). Instead I created another BookFactory.cs to hold the data and in the Uicontrol i used
<ObjectDataProvider x:Key="theBook" ObjectType="local:BookFactory" MethodName="GetTheBook" />
<Grid x:Name="grid1" DataContext="{StaticResource theBook}">
to use it. Every thing works except the button-->OnshowBook. My question is in this case how to access the Book object inside the Uicontrol XAML file ?
Update new question
I've tried to use
private void OnShowBook(object sender, RoutedEventArgs e)
{
Book thebook = ((this.FindResource("theBook") as ObjectDataProvider).Data as Book);
if (thebook != null)
MessageBox.Show(thebook.Title, thebook.Isbn);
}
The codes compiles ok but throws error at runtime. it can not find key theBook which i indeed declared.

Ok. i found out something . I need to use FindResource to access the object inside xmal.

Related

Avalonia button click event is not working

I have a simple Avalonia form:
<Window xmlns="https://github.com/avaloniaui"
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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaExperiment.MainWindow"
Title="AvaloniaExperiment">
<StackPanel>
<TextBlock>Welcome to Avalonia!</TextBlock>
<Button Name="btn" Click="btn_OnClick">Fred!</Button>
</StackPanel>
</Window>
And a method in the code behind (I want to do things this way until I become familiar with Avalonia, then maybe I'll try MVVM):
private void btn_OnClick()
{
btn.Text = "Ginger";
}
However I get these compile errors:
The name btn does not exist in the current context (in the code behind)
Unable to find suitable setter or adder for property Click of type Avalonia.Controls:Avalonia.Controls.Button for argument System.Private.CoreLib:System.String, available setter parameter lists are:
System.EventHandler`1[[Avalonia.Interactivity.RoutedEventArgs, Avalonia.Interactivity, Version=0.9.0.0, Culture=neutral, PublicKeyToken=null]] (in the XAML)
Unable to find suitable setter or adder for property Command of type Avalonia.Controls:Avalonia.Controls.Button for argument System.Runtime:System.String, available setter parameter lists are:
Avalonia.UnsetValueType
Avalonia.Data.IBinding
System.Windows.Input.ICommand (also in the XAML)
What could I be doing wrong in hooking up this event handler?
The sender is the button you just clicked, so typecast sender to Button and set its Content property (not Text) to whatever you want to.
public void btn_OnClick( object? sender, RoutedEventArgs args )
{
( sender as Button )!.Content = "Ginger";
}
No need to look it up in the tree or anything else, this way you can reuse the same code behind for all your buttons, and for instance, depending on which button it is, set different names or styles, or other properties, etc.
More advanced:
public void btn_OnClick( object? sender, RoutedEventArgs args )
{
var button = ( sender as Button )!;
switch ( button.Name )
{
case "btn":
{
button.Content = "Ginger";
}
break;
case "otherBtn":
{
button.Content = "Ale";
}
break;
default:
{
button.Content = "No clue which Button you are!";
}
break;
}
}
have you tried...
public void btn_OnClick(object sender, RoutedEventArgs e)
{
btn.Text = "Ginger";
}
You should add a ControlLink in the Parent Control Constructor
like this:
public class AnyParentControl
{
Button btn; // for the class
public AnyParentControl() // constructor
{
InitializeComponent(); // necessary method for Avalonia
btn = this.Find<Button>("The Control Name on XAML File");
btn.Click += Cbtn_Click; // event link
}
}
Greetings from Peru :D
Button does not have Text property. It does have Content.
btn.Content = "Ginger";

Access XAML binding in code-behind

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.

Declaring a new object via variable name

I'm fairly new to C# (and programming in general) so stick with me if I make any huge errors or talk complete bull.
So what I'm trying to do is have a private void that resizes the background image of a button. I send the name of the button to the private void via a string. Anyway, the code looks something like this:
ButtonResize("Zwaard");
protected void ButtonResize(string Button)
{
string ButNaam = "btn" + Button;
Button Butnaam = new Button();
Butnaam.Text = ButNaam;
if (Butnaam.BackgroundImage == null)
{
return;
}
else
{
var bm = new Bitmap(Butnaam.BackgroundImage, new Size(Butnaam.Width, Butnaam.Height));
Butnaam.BackgroundImage = bm;
}
}
But it doesn't work like that. I can't seem to find a way to declare a new object named the value I have in a string. What I want my code to do is instead of making a button called "Butnaam", I want it to create a button called btnZwaard (the value of the string Butnaam).
How do I tell C# I want the value of the variable to be the name of a new button, not literally what I type?
Thanks in advance.
Are you looking for something like this? By passing the Button to the method you can then act on the object. If this is what you are looking for then you should read Passing Reference-Type Parameters
protected void ButtonResize(Button button)
{
if (button != null && button.BackgroundImage != null)
{
button.BackgroundImage = new Bitmap(button.BackgroundImage, new Size(newWidth, newHeight));
}
}
A string is a piece of text. You subsequently refer to it as a class, which is wrong. Assuming it were right you create a new button rather than "resize its image".
What you want to do to get you started is create a new function in the same class as the dialog that has the button. That function can resize the image of the control.
Edit: this doesn't seem like a good starting point for learning a language, btw. Please find a good online tutorial for starting in C# (e.g. a hello world application).

Multiple Views of Observable Collection with Datagrid

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).

how to add single item to observablecollection

I am having trouble using the Add method for an ObservableCollection to simply add a new string value to the observablecollection upon a click event. I create my ObservableCollection in a Settings.cs class and then reference that observablecollection throughout multiple pages in my wp7.1 project. This system has worked perfectly for when I need to add several items of one observablecollection to another, either setting one equal to the other or using .Union depending on the purpose needed. In this case though, I am attempting to add a single string item to my ObservableCollection of type string. My code is as follows
Settings.cs
public static Setting<ObservableCollection<string>> Favorites = new Setting<ObservableCollection<string>>("Favorites", null);
Favorites.xaml
<ListBox x:Name="FavoritesListBox" Grid.Row="1" ItemsSource="{Binding}" Margin="12,0,12,0"
SelectionChanged="FavoritesListBox_SelectionChanged">
FavoritesPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string favorUrl = null;
NavigationContext.QueryString.TryGetValue("curUrl", out favorUrl);
if (favorUrl != null )
{
//This works but the FavoritesListBox items are cleared upon new page navigation or closing
//this.FavoritesListBox.Items.Add(favorUrl);
//This does not work!?
//if (Settings.Favorites.Value == null)
//{
// //Settings.Favorites.Value.Add(favorUrl);
//}
//else
//{
// Settings.Favorites.Value.Add(favorUrl);
//}
}
//base.OnNavigatedTo(e);
}
private void FavoritesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.NavigationService.Navigate(new Uri("/MainPage.xaml?favUrl=" + e.AddedItems[0], UriKind.Relative));
}
using the .Add method in FavoritesPage.xaml.cs does not give me any coding errors but when debugging I get a NullReferenceException. I also tried using .Insert and that did not work either. Please help this seems to be an easy fix but I have not been able to figure this out! Thanks in advance!
You're referencing a null object after confirming that it is null!
if (Settings.Favorites.Value == null)
{
Settings.Favorites.Value.Add(favorUrl); // throws NullReferenceException
// because Value is null
}
You need to do this:
if (Settings.Favorites.Value == null)
{
Settings.Favorites.Value = new ObservableCollection<string>();
}
Settings.Favorites.Value.Add(favorUrl);
Alternately, you can change the initialization from
public static Setting<ObservableCollection<string>> Favorites =
new Setting<ObservableCollection<string>>("Favorites", null);
to
public static Setting<ObservableCollection<string>> Favorites =
new Setting<ObservableCollection<string>>("Favorites",
new ObservableCollection<string>());
This way you can avoid the null check.

Categories