I have the following design in a page :
<CollectionView ItemsSource="{Binding Results}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="viewmodel:MyViewModel">
<Frame>
<HorizontalStackLayout Grid.Column="1">
<Label Text="{Binding Title}"></Label>
<Button Text="Do something" IsVisible="{Binding IsVisibleFlag}" CommandParameter="{Binding Identifier}" Clicked="ButtonClicked_EventHandler"></Button>
</HorizontalStackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
So it's basically a button bound to some results. Then I'm trying to catch the clicked EventHandler with a parameter as follows:
void ButtonClicked_EventHandler(object sender, EventArgs e)
{
ClickedEventArgs P = (ClickedEventArgs)e; // << exception here
string Identifier = P.Parameter.ToString();
// Do something
}
The button, the binding and the layout work just fine however when clicking the button, I'm having an exception in the line where I parse EventArgs into ClickedEventArgs to get the value of the parameter. I'm getting the error :
System.InvalidCastException : 'Unable to cast object of type 'System.EventArgs' to type 'Microsoft.Maui.Controls.ClickedEventArgs'.'
Does anyone know what I'm doing wrong please ?
Thanks.
A CommandParameter, as the name implies, is intended to be used with a Command, not an event handler. However, you can do something similar like this
Button btn = (Button)sender;
var parameter = (string)btn.CommandParameter;
Related
I have a list of devices and in that list user will select which COM port represents which device, each device has its own StackPanel shown below:
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Width="140" Text="IMT" VerticalAlignment="Center"/>
<ComboBox Width="250" Margin="0,0,40,0" x:Name="FM_list" SelectionChanged="DeviceSelected"/>
<TextBlock x:Name="FM_selection" Margin="0,0,40,0" Width="80 "/>
<Button Background="Red" Width="50" Click="Port_selected" x:Name="FM_selection1"/>
</StackPanel>
After user makes his selection in the ComboBox it is verified by clicking an adjecent Button.
I'd like it so that when the Button is clicked x:Name of the TextBlock (or an alternate way of referencing) is passed to the Port_selected function so I can get the correct device when calling TextBox.Name on the sender.
I could a seperate x:Name for each of those buttons and a dictionary to match which button matches which TextBlock and which StackPanel, but I'd like to know how to do without that workaround. Right now I just strip the last char of the Button's x:Name...
Port_selected is an event handler and you cannot pass a string value to this one.
If you are familiar with the MVVM design pattern there are better ways to solve this, for example using commands and command parameters, but the easiest solution given your current setup is probably to get a reference to the TextBlock in the event handler and then get the value of its Name property, e.g.:
private void Port_selected(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
StackPanel stackPanel = btn.Parent as StackPanel;
if (stackPanel != null)
{
TextBlock textBlock = stackPanel.Children
.OfType<TextBlock>()
.FirstOrDefault(x => !string.IsNullOrEmpty(x.Name));
if (textBlock != null)
{
string name = textBlock.Name;
//...
}
}
}
I am trying to figure out how to use the ScrollTo method that is available with list views. I believe I have the last two arguments correct, but am not quite sure if I have the first argument correct.
Here is the xaml with my list view for my view...
<ListView
x:Name="MyList"
Grid.Row="0"
Grid.RowSpan="4"
Grid.Column="0"
Grid.ColumnSpan="8"
ItemsSource="{Binding History}"
ItemTapped="OnItemTapped"
RowHeight="60">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="8">
<Label
Text="{Binding MessageTitle}"
FontAttributes="Bold" />
<Label
Text="{Binding MessageContents}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is part of the code behind for that view where I am trying to use .ScrollTo
private void ScrollToBottomClicked(object sender, EventArgs e)
{
List.ScrollTo(MyList.ItemsSource, ScrollToPosition.End, true);
}
Here is the ItemSource, which is an observable collection in my view model
public ObservableCollection<HistoryMessage> History
{
get
{
return History;
}
set
{
History = value;
}
}
When the ScrollToBottom button is clicked I am wanting the list view display to jump the the most recent item added. Any help would be greatly appreciated!
For example:
In my code behind I have the following method, in my case is void because I only call when I load all the list
public void ScrollDown(HistoryMessage historyMessage)
{
List.ScrollTo(historyMessage, ScrollToPosition.End, false);
}
So, when all I get all the list I call this method
ScrollDown(History.LastOrDefault());
If you have a button and you want me to show you the closest item of the list, you should make a query to your API to see if there is any element added and later call this method and sent the lastOrDefault element.
If what you want is the first element you need change List.ScrollTo(historyMessage, ScrollToPosition.End, false) to List.ScrollTo(historyMessage, ScrollToPosition.Start, false) and sent de first element in your observable collection
Getting the SelectedItem's Tag property when Item has been selected..
I have this in my XAML:
<ListView IsItemClickEnabled="True" x:Name="settigns_listView" Margin="15,270,0,0" ItemClick="settigns_listView_ItemClick">
<ListViewItem Tag="credits" Margin="0,0,30,0" BorderThickness="0,0,0,3" BorderBrush="#FF353534" FontSize="26.667" Content="Credits"/>
<ListViewItem Tag="reset" Margin="0,10,30,0" BorderThickness="0,0,0,3" BorderBrush="#FF353534" FontSize="26.667" Content="Reset game"/>
</ListView>
And within the ItemClick event I have this:
private void settigns_listView_ItemClick(object sender, ItemClickEventArgs e)
{
ListViewItem listViewItem = settigns_listView.SelectedItem as ListViewItem;
string data = listViewItem.Tag.ToString();
}
However for this line string data = listViewItem.Tag.ToString(); I get this error "Object reference cannot be null" it seems my listViewItem is null, but i dont understand why?
Does anyone understand what I am doing wrong?
go with debugger and check:
is settigns_listView.SelectedItem null when you access it?
if not, is it a ListViewItem? (it should be...).
Maybe you need to register for SelectionChanged event of the listview instead of the Click event of the ListViewItem
XAML code:
<TextBlock Text="Country" Foreground="white" TextAlignment="Right" Grid.Row="2" Grid.Column="0" />
<TextBox
x:Name="txtCountries"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
Margin="2, 2, 2, 2"
Text="{Binding PhysicalDeliveryParameters.Countries, Converter={StaticResource EnumerableToTextConverter}, ConverterParameter='...'}"
IsReadOnly="True">
</TextBox>
<Button
Grid.Row="2"
Grid.Column="3"
Content="..."
HorizontalAlignment="Left"
Tag="Countries"
Click="ButtonBase_OnClick" />
C# code :
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
PhysicalDeliveryParametersViewModel pvm = GetViewModel();
GenericObservableCollection<SelectableItem> items = pvm.Countries;
PhysicalDeliveryParametersDlg dlg = new PhysicalDeliveryParametersDlg(items);
dlg.Closed += (o, args) =>
{
BindingExpression binding = txtCountries.GetBindingExpression(TextBox.TextProperty);
if(null != binding)
binding.UpdateSource();
};
dlg.ShowDialog();
}
When I click on the button, the ButtonBase_OnClick() method executes : a dialog appears (PhysicalDeliveryParametersDlg class) and I choose some values. The binded data (PhysicalDeliveryParameters.Countries, which is an ObservableCollection) is updated, but not the Text property of my TextBox... Did I do something wrong ?
PS : I'm not sure I use the best method to create a modal window in Silverlight, could you give me some advice ?
It looks like the problem is that PropertyChanged never gets raised on the "Countries" property, so the view doesn't know it needs to update. (Actually, it probably wouldn't help to raise "PropertyChanged" in this case -- since the object reference has not changed, I believe the runtime would ignore it.)
I would just add another property "CountriesString" or similar:
Text="{Binding PhysicalDeliveryParameters.CountriesString}"
Update the property whenever is appropriate:
dlg.Closed += (o, args) =>
{
pvm.CountriesString = string.Join(", ", pvm.Countries);
};
I am trying to pass UserName by CommandParameter.
<GridViewColumn Width="Auto" Header="UserName" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Click="Button_Click" Content="..." CommandParameter="{Binding Path=UserName}"></Button> </DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
But here I am not sure what do I have to use to catch it...
private void Button_Click(object sender, RoutedEventArgs e)
{
// How to get UserName here?
}
Any clue?
Thank you!
Best you can do with such approach is this:
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = (Button) sender;
var userName = button.CommandParameter;
}
Though it will do the job but it is not how CommandParameter is supposed to be used. CommandParameter is supposed to be passed as parameter to button's command. So if you will use Command instead of handling Button.Click your command will receive your data in its Execute and CanExecute handlers. See samples for commands&buttons in google. Here is first link from google for example: How to use Commands in WPF
If you are going to use the click handler, why not bind the Button's Tag property to UserName? A little more along the lines of what the Tag property was intended than a misuse of Commanding principals.
<Button Click="Button_Click" Content="Button Content" Tag="{Binding UserName}"/>
private void Button_Click(object Sender, RoutedEventArgs e)
{
var Button = (Button)Sender;
var UserName = (string)Button.Tag;
}