How To Bind Two different Items - c#

how can two different items in a list be bound to one another hence when one is deleted its bound patner is also deleted.The question may sound simple, but the items are in two different lists.
public class MyClass{
public StackLayout SavedHoursLayout = new StackLayout {};
public Label RemoveHoursLabel;
public TapGestureRecognizer RemoveTapped;
public Grid HoursRemoveGrid;
public Button AddHoursButton = new Button();
public Label Correct = new Label{Text="Correct"};
public list<Label> ItemsLayout = new list<Label>();
public MyClass()
{
Content = new StackLayout
{
Children = { AddHoursButton,SavedHoursLayout }
}
AddHoursButton.Clicked+=AddHoursButton_Clicked;
}
public void AddSavedHours()
{
Label Time = new Label { };
RemoveHoursLabel = new Label {
Text="remove",TextColor=Color.Red,FontAttributes=FontAttributes.Italic};
HoursRemoveGrid = new Grid();
RemoveTapped = new TapGestureRecognizer();
this.BindingContext = HoursRemoveGrid;
HoursRemoveGrid.Children.Add(Time,0,0);
HoursRemoveGrid.Children.Add(RemoveHoursLabel,1,0);
SavedHoursLayout.Children.Add(HoursRemoveGrid);
RemoveHoursLabel.GestureRecognizers.Add(RemoveTapped);
RemoveTapped.Tapped += RemoveTapped_Tapped;
ItemsLayout.Children.Add(Correct);
void RemoveTapped_Tapped(object sender, EventArgs e)
{
var grid = (sender as Label).Parent as Grid;
int position = SavedHoursLayout.Children.IndexOf(grid);
SavedHoursLayout.Children.RemoveAt(position);
}
}
private void AddHoursButton_Clicked(object sender, System.EventArgs e)
{
AddSavedHours();
}
}
Question
Now when i click RemoveHourLabel, i want to remove the Label Correctin ItemsLayoutcorresponding to the RemovehoursGrid.
NB
There are already a number of Labels in ItemsLayout, so each Label Correct does not have the same index as that of its correspondent RemoveHoursGrid.

Solution:
You can create a new list<Label> to hold the labels so that it will have the same index as that of its correspondent RemoveHoursGrid.
First, create a new list, let's call it tempItemsLayout :
public List<Label> tempItemsLayout = new List<Label>();
Then in your method AddSavedHours, Add the correct to tempItemsLayout too:
public void AddSavedHours()
{
//...other codes
RemoveHoursLabel.GestureRecognizers.Add(RemoveTapped);
RemoveTapped.Tapped += RemoveTapped_Tapped;
ItemsLayout.Add(Correct);
//Add the correct to tempItemsLayout too
tempItemsLayout.Add(Correct);
//...other codes
}
Then in your RemoveTapped_Tapped, Remove the label from both tempItemsLayout and ItemsLayout:
void RemoveTapped_Tapped(object sender, EventArgs e)
{
var grid = (sender as Label).Parent as Grid;
int position = SavedHoursLayout.Children.IndexOf(grid);
SavedHoursLayout.Children.RemoveAt(position);
Label tempLabel = tempItemsLayout[position];
//Remove the label from both tempItemsLayout and ItemsLayout
tempItemsLayout.Remove(tempLabel);
ItemsLayout.Remove(tempLabel);
}
Try and let me know if it works.

Related

C# dynamically add button event handler with arguments

I've seen many examples of how to add a click event to a dynamically created button, but none of the examples show how to pass arguments with the event.
I am new to C# and just looking for a simple solution. See the code below:
public partial class Main : Form {
public Main() {
InitializeComponent();
// Add a button dynamically
Button Test = new Button();
this.Controls.Add(Test);
Test.Left = 0;
Test.Top = 0;
Test.Width = 100;
Test.Height = 20;
Test.Text = "Hello";
int param1 = 1;
string param2 = "Test";
// Add click event handler with parameters ????
// I know this syntax is wrong, but how do I get param1 & 2
// into the Test_Click ????
Test.Click += Test_Click(param1,param2);
}
private void Test_Click(int param1, string param2) {
MessageBox.Show(param1.ToString() + ": " + param2);
}
You do not provide arguments when adding the event but you provide it in the event it self, in this case the click event arguments are:
private void Test_Click(object sender, EventArgs e)
{
}
the first argument is usually object sender while the second changes depending on the event type, in case of click event it's "EventArgs e"
and for the adding event :
Test.Click += Test_Click;
Hope i helped you.
Ok. I know this is a little ugly, but this is what I came up with from the comments above. If there is a better way to do this, please let me know:
public partial class Main : Form {
public Dictionary<object, Tuple<int, string>> Params = new Dictionary<object, Tuple<int,string>>();
public Main() {
InitializeComponent();
// Add a button dynamically
Button Test = new Button();
this.Controls.Add(Test);
Test.Left = 0;
Test.Top = 0;
Test.Width = 100;
Test.Height = 20;
Test.Text = "Hello";
Test.Name = "Test";
// Add click event handler with parameters
Params.Add(Test, new Tuple<int, string>(1, "Test"));
Test.Click += Test_Click;
}
private void Test_Click(object sender, EventArgs e) {
Tuple<int,string> value;
if (Params.TryGetValue(sender, out value)) {
MessageBox.Show(value.Item1.ToString() + ": " + value.Item2);
}
else {
MessageBox.Show(sender.ToString() + " not found.");
}
}
Here is another solution I came up with without using Dictionary or Tuple by adding the button and the parameters together into a class structure. I like this one better:
class MyButton {
private Button oButton;
private int iParam1;
private string sParam2;
public MyButton(Form Me, int Left, int Top, int Width, int Height, string Text, int Param1, string Param2) {
oButton = new Button();
Me.Controls.Add(oButton);
oButton.Left = Left;
oButton.Top = Top;
oButton.Width = Width;
oButton.Height = Height;
oButton.Text = Text;
iParam1 = Param1;
sParam2 = Param2;
oButton.Click += Click;
}
private void Click(object sender, EventArgs e) {
MessageBox.Show(iParam1.ToString() + ": " + sParam2);
}
}
public partial class Main : Form {
public Main() {
InitializeComponent();
MyButton Test = new MyButton(this, 0, 0, 100, 20, "Hello", 1, "Test");
}
}
Here's a bit cleaner version of what I was talking about (sorry, I wasn't near a computer earlier). The initial implementation of Tuple was truly clunky; it's since become part of the language.
I started with the auto-generated:
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
}
And then added this to the form class:
private Dictionary<string, (int param1, string param2)> _parametersMap = new Dictionary<string, (int param1, string param2)>();
If you are using the latest compiler, you can simplify this to:
private Dictionary<string, (int param1, string param2)> _parametersMap = new();
Then I added a method to the form class that the click handler will call:
public void ShowSomething (string buttonName, int i, string s)
{
MessageBox.Show(this, $"Button: {buttonName} cliked: i={i}, s = {s}");
}
All button click handlers have the same method signature. It's determined by the code that raises the click event. So...
public void OnDynamicButtonClick (object sender, EventArgs e)
{
if (sender is Button button)
{
var (param1, param2) = _parametersMap[button.Name];
ShowSomething(button.Name, param1, param2);
}
}
Notice that the if statement uses a pattern matching if mechanism. The code within the if sees button as the sender cast to a Button.
Then, to match your initial code, I put this in the form constructor (after InitializeComponent. It really doesn't belong there. It should be in some event handler, at the very least the Form Load handler, more likely wherever it is that you want to create the buttons (creating them in the constructor kind of defeats the idea of dynamically constructing them):
var firstButton = new Button
{
Name = "FirstButton",
Text = "First",
Location = new Point(100, 100),
Size = new Size(200, 50),
Parent = this,
};
firstButton.Click += OnDynamicButtonClick;
_parametersMap.Add(firstButton.Name, (42, "Test1"));
Controls.Add(firstButton);
var secondButton = new Button
{
Name = "SecondButton",
Text = "Second",
Location = new Point(100, 300),
Size = new Size(200, 50),
Parent = this,
};
secondButton.Click += OnDynamicButtonClick;
_parametersMap.Add(secondButton.Name, (111, "Test2"));
Controls.Add(firstButton);
Note that I created two buttons, both pointing to the same button click handler (that's the point of the sender argument.
If you are going to be doing this a lot, an alternative approach would be to sub-class the Button class, adding your Param1 and Param2 as properties:
public class MyButtonSubClass : Button
{
public int Param1 { get; set; }
public string Param2 { get; set; }
}
(hopefully giving them more meaningful names)
Instances of your subclass are Buttons, just with two extra properties, so you can do this:
var firstButton = new MyButtonSubclass
{
Name = "FirstButton",
Text = "First",
Location = new Point(100, 100),
Size = new Size(200, 50),
Parent = this,
Param1 = 42,
Param2 = "Some Test",
};
firstButton.Click += OnDynamicButtonClick;
Controls.Add(firstButton);
//same for the second button
And then change the click event handler to:
public void OnDynamicButtonClick (object sender, EventArgs e)
{
if (sender is MyButtonSubclass button)
{
ShowSomething(button.Name, button.Param1, button.Param2);
}
}
And the program will appear to work in the same way.
Or, at this point, you could change the event handler to look like:
public void OnDynamicButtonClick(object sender, EventArgs e)
{
if (sender is MyButtonSubclass button)
{
MessageBox.Show(this, $"Button: {button.Name} cliked: i={button.Param1}, s = {button.Param1}");
}
}

How to create grid on button in Xamarin Forms

So I need to create a custom button, on what i have to create grid and on this grid I need to create several labels with specific informations.
This is code where i`m adding child to button
private void HighlightTodayDay()
{
Label label1 = new Label()
{
BackgroundColor = Color.DarkRed,
Text = "lbl1"
};
Label label2 = new Label()
{
BackgroundColor = Color.Gold,
Text = "lbl2"
};
if ((DateTime.Today.Year == actualVisibleMonth.Year) && (DateTime.Today.Month == actualVisibleMonth.Month))
{
foreach (var child in Children.Reverse())
{
if (child.ClassId.ToString() == ("actualDayButtonID" + DateTime.Today.Day.ToString()) && child.IsEnabled == true)
{
DayButton todayDayButton = dayButtonsList[DateTime.Today.Day + shiftOfFirstDay];
todayDayButton.TextColor = Color.FromHex("#0f0");
//upto this line everything is working as it should
todayDayButton.insideGrid.Children.Add(label1, 0, 0); //do nothing
todayDayButton.insideGrid.Children.Add(label2, 0, 1); //do nothing
return;
}
}
}
}
and here is code from "custom" button
class DayButton : Button
{
public string EventDate;
public string EventStartTime;
public string EventEndTime;
public string EventShift;
public string EventName;
public string EventDescription;
public Grid insideGrid;
public DayButton()
{
insideGrid = new Grid();
insideGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
insideGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(3, GridUnitType.Star) });
insideGrid.Parent = this;
}
}
It should look like this click to see image
you can add a GestureRecognizer to the Grid and then bind the tap event.
Ex of button code (now a ContentView):
class DayButton : ContentView
{
public string EventDate;
public string EventStartTime;
public string EventEndTime;
public string EventShift;
public string EventName;
public string EventDescription;
public Grid insideGrid;
public event EventHandler Clicked;
private TapGestureRecognizer _buttonTap;
private Lable _ButtonText;
public DayButton()
{
_ButtonText = new Lable
{
Text = EventName
}
insideGrid = new Grid
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
RowSpacing = 0,
RowDefinitions =
{
new RowDefinition {Height = GridLength.Auto} //0
},
ColumnDefinitions =
{
new ColumnDefinition {Width = GridLength.Star} //0
}
};
//Add your elements to the grid
insideGrid.Children.Add(_ButtonText, 0, 1, 0, 1)
//Set the grid as the content of this view
Content = insideGrid;
_buttonTap = new TapGestureRecognizer();
this.GestureRecognizers.Add(_buttonTap);
_buttonTap.Tapped += ButtonClicked;
}
}
private async void ButtonClicked(object sender, EventArgs e)
{
if (this.IsEnabled)
{
Clicked?.Invoke(this, e);
}
}
Then you can bind the "Clicked" event where you implement the button.
private DayButton _btn1 = new DayButton(){ EventName = "FooBaar"};
protected override void OnAppearing()
{
_btn1.Clicked += OnDayBtnClicked;
base.OnAppearing();
}
protected override void OnDisappearing()
{
_btn1.Clicked -= OnDayBtnClicked;
base.OnDisappearing();
}
private void OnDayBtnClicked(object sender, EventArgs e)
{
//What to do when a button is clicked
}
I decided to dismiss button and change it to stack layout (sick!) and to this stacklayout i`m adding new label with events!
soo code look like this:
public partial class Calendar : Grid
{...
public delegate void OnDayClickedDelegate(DateTime dateOfClickedDay);
public event OnDayClickedDelegate OnDayClicked;
...
private void DayClick(DateTime clickedDate)
{
OnDayClicked(clickedDate);
}
...
private void SomeVoid()
{...
DayLayout eventInDay = dayLayoutList[dayID];
var eventLabel = new Label
{
BackgroundColor = color,
Text = name.ToUpper(),
FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
FontAttributes = FontAttributes.Bold
};
eventInDay.Children.Add(eventLabel);
...}
}
and day view
private class DayLayout : StackLayout
{
public delegate void OnClickedDelegate(DateTime dateOfClickedDay);
public event OnClickedDelegate OnClicked;
public Label DayNumber;
public DayLayout(DateTime day)
{
GestureRecognizers.Add
(
new TapGestureRecognizer
{
Command = new Command(() => OnClicked(day))
}
);
var dayNumber = new Label
{
HorizontalTextAlignment = TextAlignment.End,
VerticalTextAlignment = TextAlignment.Start,
Text = day.ToString("dd"),
FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
FontAttributes = FontAttributes.Bold
};
DayNumber = dayNumber;
this.Children.Add(DayNumber);
}
}

How to access button click inside listview datatemplate in Xamarin.Forms?

I have listview that have two views manipulated dynamically label and button, and I am trying to access button click to go to the details page on button click.
below is my custom ViewCell
protected override async void OnAppearing()
{
listClass.ItemsSource = list;
listClass.ItemTemplate = new DataTemplate(typeof(ItemTemplateViewCell ));
}
public class ItemTemplateViewCell : ViewCell
{
Label NameLbl = new Label();
StackLayout sLayout = new StackLayout ();
Button btnViewcell = new Button {Text = "Show class details"};
public ItemTemplateViewCell()
{
NameLbl.SetBinding(Label.TextProperty, "Name");
sLayout.Children.Add(NameLbl);
btnViewcell.Clicked += (s, e) =>
{
// Navigation.PushAsync(new Home()); //I can not using this line
// does not exist in the current context, why cant i navigate to
// another page from inside datatemplate in List view
};
sLayout.Children.Add(btnViewcell);
this.View = sLayout;
}
}
You can pass the Navication to ViewCell through constructor:
public class ItemTemplateViewCell : ViewCell
{
// Navigation Mermber
INavigation MyNavigation;
Label NameLbl = new Label();
StackLayout sLayout = new StackLayout ();
Button btnViewcell = new Button {Text = "Show class details"};
public ItemTemplateViewCell(INavigation navigation)
{
MyNavigation = navigation;
NameLbl.SetBinding(Label.TextProperty, "Name");
sLayout.Children.Add(NameLbl);
btnViewcell.Clicked += ButtonShowDetails_Clicked;
sLayout.Children.Add(btnViewcell);
this.View = sLayout;
}
private void ButtonShowDetails_Clicked(object sender, EventArgs e)
{
MyNavigation.PushAsync(new Home());
}
}
Then pass the Navication through the delegate Function
protected override async void OnAppearing()
{
listClass.ItemsSource = list;
listClass.ItemTemplate = new DataTemplate(Function) ;
}
public object Function()
{
return new ItemTemplateViewCell (Navigation);
}
Then you can access the Navigation object in ViewCell
Simply you are able to use CommandParameterProperty with binding :
Example:
ListViewClass.ItemTemplate = new DataTemplate(() =>
{
var GoHomeButton = new Button { Text = "Go Home", HorizontalOptions = LayoutOptions.StartAndExpand };
GoHomeButton.SetBinding(Button.CommandParameterProperty, new Binding("Name"));
GoHomeButton.Clicked += Gohome;
//return new view cell
}
private void Gohome(object sender, EventArgs e)
{
Navigation.PushAsync(new Home());
}
For more info check the following Link:
http://www.c-sharpcorner.com/blogs/handling-child-control-event-in-listview-using-xamarinforms1
What I think is going wrong is that you are performing an action immediately in the initialization of the class. You should split it up so like below. Another issue is that you do initialization of variables outside of the methods. This might go okay, but I prefer the below code:
public class ItemTemplateViewCell : ViewCell
{
Label nameLbl;
StackLayout sLayout;
Button btnViewcell;
public ItemTemplateViewCell()
{
nameLbl = new Label()
sLayout = new StackLayout ()
btnViewcell = new Button {Text = "Show class details"};
NameLbl.SetBinding(Label.TextProperty, "Name");
sLayout.Children.Add(NameLbl);
btnViewcell.Clicked += OnButtonClicked;
sLayout.Children.Add(btnViewcell);
this.View = sLayout;
}
void OnButtonClicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Home());
}
}
I think this should work in your case but I can't be sure. I don't think what you posted is one on one your code since I don't see any initialization of s and e in your code and as mentioned in the comments the comment in your code would cause issues if you really put it as in the question. Also you don't share the code of the Home class. Something might be wrong at that end.
Navigation is not available inside of a ViewCell, this means you can't access it right from your ViewCell. But this should work:
App.Current.MainPage.Navigation.PushAsync(new Home());
The whole code:
protected override async void OnAppearing()
{
listClass.ItemsSource = list;
listClass.ItemTemplate = new DataTemplate(typeof(ItemTemplateViewCell ));
}
public class ItemTemplateViewCell : ViewCell
{
Label NameLbl = new Label();
StackLayout sLayout = new StackLayout ();
Button btnViewcell = new Button {Text = "Show class details"};
public ItemTemplateViewCell()
{
NameLbl.SetBinding(Label.TextProperty, "Name");
sLayout.Children.Add(NameLbl);
btnViewcell.Clicked += (s, e) =>
{
App.Current.MainPage.Navigation.PushAsync(new Home());
};
sLayout.Children.Add(btnViewcell);
this.View = sLayout;
}
}

Combobox contents based on another combobox value

I have two comboboxes where the first one has categories (and I can fill that easily from the source file). The trick is having the second combobox show only the items that are associated with the chosen category from the first combobox. For example:
cb1 is populated from a sourcefile with the category values 1, 2, 3 & 4 and cb2 is populated with values A,B,C,D,E,F,G,H
What I am failing at doing is limiting what is seen in cb2. So when cb1's value is "1", I only want "A" and "B" to be visible in cb2, and if cb1 changes to "2" I only want "C" and "D" to be visible.
For winforms :
If you have a form with 2 combo boxes (cb1, cb2) you could use something like this? (obviously modified to support your data objects).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//create a list for data items
List<MyComboBoxItem> cb1Items = new List<MyComboBoxItem>();
//assign sub items
cb1Items.Add(new MyComboBoxItem("1")
{
SubItems = { new MyComboBoxItem("A"), new MyComboBoxItem("B") }
});
cb1Items.Add(new MyComboBoxItem("2")
{
SubItems = { new MyComboBoxItem("C"), new MyComboBoxItem("D") }
});
cb1Items.Add(new MyComboBoxItem("3")
{
SubItems = { new MyComboBoxItem("E"), new MyComboBoxItem("F") }
});
cb1Items.Add(new MyComboBoxItem("4")
{
SubItems = { new MyComboBoxItem("G"), new MyComboBoxItem("H") }
});
//load data items into combobox 1
cb1.Items.AddRange(cb1Items.ToArray());
}
private void cb1_SelectedIndexChanged(object sender, EventArgs e)
{
//get the combobox item
MyComboBoxItem item = (sender as ComboBox).SelectedItem as MyComboBoxItem;
//make sure no shinanigans are going on
if (item == null)
return;
//clear out combobox 2
cb2.Items.Clear();
//add sub items
cb2.Items.AddRange(item.SubItems.ToArray());
}
}
public class MyComboBoxItem
{
public string Name;
public List<MyComboBoxItem> SubItems = new List<MyComboBoxItem>();
public MyComboBoxItem(string name)
{
this.Name = name;
}
public override string ToString()
{
return Name;
}
}

How do i handle the event of a selected pivot item in Windows Phone

I have an app with 3 PivotItems and an ApplicationBar.
I want to hide the ApplicationBar when the PivotItems 2 and 3 are selected, and show the ApplicationBar when the first PivotItem is selected.
I don't know why this question has been down voted. The question sense may be wrong and it can be edited. I got the solution for you #user3847141. Here you go.
PivotItem pivot = null;
private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ApplicationBar appBar = new ApplicationBar();
ApplicationBarIconButton appBarIconButton = new ApplicationBarIconButton();
pivot = (PivotItem)(sender as Pivot).SelectedItem;
switch(pivot.Header.ToString())
{
case "item1":
appBar.Mode = ApplicationBarMode.Default;
appBarIconButton.IconUri = new Uri("/appbar.close.png", UriKind.RelativeOrAbsolute);
appBarIconButton.Text = "Close";
appBar.Buttons.Add(appBarIconButton);
this.ApplicationBar = appBar;
break;
case "item2":
appBar.Mode = ApplicationBarMode.Minimized; // To minimize AppBar
appBar = null; // Delete Application Bar
this.ApplicationBar = appBar;
break;
case "item3":
appBar.Mode = ApplicationBarMode.Minimized;
appBar = null;
this.ApplicationBar = appBar;
break;
}
}
You can achieve this through Selection_Changed event in Pivot. Hope it helps.
First give names for your pivot items then you need to create a PivotCallbacks class
class PivotCallbacks
{
public Action Initiate { get; set; }
public Action OnAvtivated { get; set; }
public Action<CancelEventArgs> OnBackKeyPress { get; set; }
}
Then in your page constructor add the following.
public MainPage()
{
InitializeComponent();
_callbacks = new Dictionary<object, PivotCallbacks>();
_callbacks[pivotItem1] = new PivotCallbacks
{
Initiate = ShowAppbar,
OnAvtivated = ShowAppbar
};
_callbacks[pivotItem2] = new PivotCallbacks
{
OnAvtivated = HideAppbar
};
_callbacks[pivotItem3] = new PivotCallbacks
{
OnAvtivated = HideAppbar
};
foreach (var callbacks in _callbacks.Values)
{
if (callbacks.Initiate != null)
{
callbacks.Initiate();
}
}
}
(here ShowAppbar and HideAppbar are the methods that has the logic to show/hide the app bar)
Then when ever the selection is changed in the current pivot item you need to call the appropriate function attached to it. to do that in the Selection changed event of the pivot item add the following code.
private void pivotItemMomento_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
PivotCallbacks callbacks;
if (_callbacks.TryGetValue(momentoPivot.SelectedItem, out callbacks) && (callbacks.OnAvtivated != null))
{
callbacks.OnAvtivated();
}
}
So when onActivated is called the methods associated to that Action is called in the appropriate pivot item. You can do the same to the other actions (eg. OnBackKeyPress) as well. Hope this helps.

Categories

Resources