I am working on a C# WPF project and I am storing some items in an SQLite database, when the program loads, it then retrieves the items from the database and adds the items to the menu. What I then need to do is to allow the user to click on one of the added menu items and something is done based on what was clicked. I can't find anything on how to do this, below is the code for how I am adding the menu item to the menu programatically.
StoredDBConnectionManager storedDbConnectionManager = new StoredDBConnectionManager(Properties.Settings.Default.app_dbPassword);
List<string> connections = storedDbConnectionManager.getStoredConnections();
foreach (string connection in connections)
{
mnuFileDBConnections.Items.Add(connection);
}
Thanks for any help you can provide.
Here's an example:
XAML:
<Menu Height="23" HorizontalAlignment="Left" Name="menu1" VerticalAlignment="Top" Width="200" />
Code behind:
public MainWindow() {
InitializeComponent();
MenuItem item = new MenuItem { Header = "test" };
item.Click += new RoutedEventHandler(item_Click);
menu1.Items.Add(item);
}
public void item_Click(Object sender, RoutedEventArgs e) {
MessageBox.Show("Hello!");
}
There should be a MenuItem control you can instantiate and use the connection as its Header or Content.
MenuItem will then have a Click event handler against it or you can set the command.
Ideally however, you should be retrieving the connections collection, setting it to a property on your model and then have the menu bound to that collection, that way it's a simple matter of making use of an ItemTemplate for the menu.
e.g.
StoredDBConnectionManager storedDbConnectionManager = new StoredDBConnectionManager(Properties.Settings.Default.app_dbPassword);
List<string> connections = storedDbConnectionManager.getStoredConnections();
foreach (string connection in connections)
{
var mi = new MenuItem()
{
Header = connection,
};
mi.Click += ConnectionMenuItemClicked;
mnuFileDBConnections.Items.Add(mi);
}
OR with binding:
<Menu ItemsSource="{Binding Connections}">
<Menu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Click="ConnectionsMenuItem_Clicked">
</MenuItem>
</DataTemplate>
</Menu.ItemTemplate>
</Menu>
foreach(string menuCaption from ...)
{
MenuItem mi=new MenuItem();
mi.Header = meniCaption;
mi.Click += (s,e) =>
{
...
}
}
Related
I'm writing a Windows desktop app using C# and WPF. I've a combobox that is used to either type in a file path or to select from previously used file paths. The files in the list can become invalid if the file is deleted or the user entered a valid file but not the desired file. They've asked for a way to remove bad entries from the combobox drop-down list. They want to right-click on an item and select Remove from the context menu.
<ComboBox x:Name="cbDocket" IsEditable="True">
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Click="cbDocket_MenuItemRemove">/>
</ContextMenu>
</ComboBox.ContextMenu>
</ComboBo>
That's the easy part. What I cannot figure out is how to determine which item they selected. Searching hasn't found any suggestions that work. Any help would be appreciated. Is there another way to do this that would be easier? Oh yes, I fairly new to both C# and WPF.
Thanks, Brian
Context menus in WPF are pretty badly broken by design, so about half of all normal cases, anything you do that actually works is a kludge. I've come up with a few; here's another that's not really all that kludgey.
XAML:
<ComboBox x:Name="cbDocket" IsEditable="True">
<ComboBox.Resources>
<ContextMenu x:Key="ItemMenu">
<MenuItem Header="Remove" Click="MenuItem_Click" />
</ContextMenu>
</ComboBox.Resources>
<!-- Some arbitrary random junk to display in the ComboBox -->
<TextBlock Text="Foo" />
<TextBlock Text="Bar" />
<!-- End of arbitrary random junk -->
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter
Property="ContextMenu"
Value="{StaticResource ItemMenu}"
/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Code behind:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
// This needs some null checking and a try catch, but this is the guts of it.
// sender should be the MenuItem as well
var menuItem = e.OriginalSource as MenuItem;
// Since we used ItemContainerStyle to give each ComboBoxItem its own
// personal ContextMenu, each ContextMenu will have its PlacementTarget
// set to the ComboBoxItem that owns it.
var cbItem = (menuItem.Parent as ContextMenu).PlacementTarget as ComboBoxItem;
// ???
//var dataItem = cbItem.DataContext;
}
You didn't mention how you're populating the combo box, so I don't know whether you've got DataContext set on the items or what. But once you have the ComboBoxItem, you can get there.
Add an event to track the mouse movement while the dropdown is open and use that when the remove is clicked.
<ComboBox x:Name="cbDocket" HorizontalAlignment="Left" Margin="33,30,0,269" Width="124" IsEditable="True">
<ComboBox.ContextMenu>
<ContextMenu Name="Menu">
<MenuItem Header="Remove" Click="cbDocket_MenuItemRemove"/>
</ContextMenu>
</ComboBox.ContextMenu>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<EventSetter Event="MouseMove" Handler="cbDocket_OnMouseMove" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Code behind:
ComboBoxItem cbiSelected = null;
private void cbDocket_MenuItemRemove(object sender, RoutedEventArgs e)
{
cbDocket.Items.Remove(cbiSelected);
}
private void cbDocket_OnMouseMove(object sender, MouseEventArgs e)
{
ComboBoxItem cbiHover = sender as ComboBoxItem;
if (cbiHover.IsHighlighted)
{//Verify the item is highlighted
cbiSelected = cbiHover;
}
}
There's a couple ways of doing this. Since you are using the Click event handler, then the source of the event is always the item that was clicked. For example:
Xaml:
<ComboBox x:Name="cbDocket" IsEditable="True">
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Click="cbDocket_MenuItemRemove">/>
</ContextMenu>
</ComboBox.ContextMenu>
</ComboBo>
Xaml.cs:
private void cbDocket_MenuItemRemove(object source, EventArgs args)
{
// Source is your item:
cbDocekt.Items.Remove(cbDocket.SelectedItem);
}
Of course you can always obtain the selected item, or even change the selected item in the combo box using the SelectedItem property.
I'm assuming you are using code behind to do this.
With the following program code you can add a context menu to a combo box. In this context menu a single item or all items of the combo box can be removed.
private ComboBox FolderComboBox;
private ContextMenu FolderHistoryContextMenu;
...
private void CreateFolderHistoryContextMenu()
{
MenuItem RemoveFolderMenuItem, RemoveAllFoldersMenuItem;
FolderHistoryContextMenu = new ContextMenu();
RemoveFolderMenuItem = new MenuItem();
RemoveFolderMenuItem.Header = "Remove folder";
RemoveFolderMenuItem.Click += new RoutedEventHandler(RemoveFolderMenuItem_Click);
FolderHistoryContextMenu.Items.Add(RemoveFolderMenuItem);
RemoveAllFoldersMenuItem = new MenuItem();
RemoveAllFoldersMenuItem.Header = "Remove all folders";
RemoveAllFoldersMenuItem.Click += new RoutedEventHandler(RemoveAllFoldersMenuItem_Click);
FolderHistoryContextMenu.Items.Add(RemoveAllFoldersMenuItem);
// The context menu is assigned to each item in the list of the combo box and not to the combo box itself.
if (FolderComboBox.ItemContainerStyle == null)
{
FolderComboBox.ItemContainerStyle = new Style(typeof(ComboBoxItem));
}
FolderComboBox.ItemContainerStyle.Setters.Add(new Setter(ContextMenuProperty, FolderHistoryContextMenu));
}
private void RemoveFolderMenuItem_Click(object sender, RoutedEventArgs e)
{
ComboBoxItem ClickedComboBoxItem = FolderHistoryContextMenu.PlacementTarget as ComboBoxItem;
if (ClickedComboBoxItem != null)
{
object ComboBoxDataItem = ClickedComboBoxItem.DataContext;
FolderComboBox.Items.Remove(ComboBoxDataItem);
}
}
private void RemoveAllFoldersMenuItem_Click(object sender, RoutedEventArgs e)
{
FolderComboBox.Items.Clear();
}
I am creating TabItems programmatically without any issue by using the following code:
var tabItem = new TabItem();
tabItem.Header = "My Tab Header";
tabItem.Content = new UserControl1();
MainTabControl.Items.Add(tabItem);
Now when a tab item added to tab control i also want to add image button at the same time with the creation of TabItem aligned at right side. How can i achieve this? thanks in advance.
EDIT:
I have tried a lot and still did not get an idea. following is my tabcontrol in xaml and ObservableCollection. When i run project tabs shows successfully but i don't know how to add images in it because in my tab control in xaml, it does not have TabItems markup and they are displaying automatically when running project. Please view my sample code and transform into my desired result. I wana conclude and close this issue, I Really thanks and appreciate help.
Following is xaml:
<TabControl ItemsSource="{Binding TabItems, Mode=TwoWay}" DisplayMemberPath="Content" HorizontalAlignment="Left" Height="73" Margin="10,25,0,0" VerticalAlignment="Top" Width="312"/>
Following is viewmodel
namespace WpfApplication1.ViewModels
{
public class VMTabControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyname)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyname));
}
[![enter image description here][1]][1]}
public VMTabControl()
{
TabItems = new ObservableCollection<clsTabs>(GetList().ToList());
}
private ObservableCollection<clsTabs> _tabItems;
public ObservableCollection<clsTabs> TabItems
{
get { return _tabItems; }
set
{
_tabItems = value;
OnPropertyChanged("TabItems");
}
}
public List<clsTabs> GetList()
{
List<clsTabs> tablist = new List<clsTabs>();
tablist.Add(new clsTabs { Content = "First", ImgPath = "path" });
tablist.Add(new clsTabs { Content = "Second", ImgPath = "path" });
return tablist;
}
}
}
In code
TabItem.Header is not limited to displaying strings - you can set any UI control on it. For example:
tabItem.Header = new Button { Content = "Click me" };
To display both text and a close button, you could use a horizontal stack panel that contains a text block and a button.
In XAML
However, UI layouts are most often written in XAML. The following XAML assumes you have an Items property in your view model, which is a collection of items. These items have a TabHeaderName and TabImagePath property. The view model should also have a RemoveTabCommand property, which is an ICommand that takes a single argument (the tab item to be removed):
<TabControl ItemsSource="{Binding Items}">
<!-- // If you only need to display a single property, you can use DisplayMemberPath.
// If you need something more fancy (such as a text-block and button next to each other),
// you'll have to provide a tab header template instead: -->
<TabControl.ItemTemplate>
<DataTemplate>
<!-- // Tab item header template (this is how each tab header will look): -->
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding TabHeaderName}" />
<Button Content="X"
Command="{Binding DataContext.RemoveTabCommand, RelativeSource={RelativeSource AncestorType=TabControl}}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<!-- // Tab item content template (this is how each tab content will look): -->
<Image Source="{Binding TabImagePath}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
If Items is an observable collection, simply adding an item to it will automatically add a tab item for it. Likewise, removing an item will remove its tab item.
Try this:
var tabItem = new TabItem();
var stack = new StackPanel();
var t = new TextBlock();
t.Text = "My Tab Header";
var i = new Image();
//i.Source = ...
stack.Children.Add(t);
stack.Children.Add(i);
tabItem.Header = stack;
tabItem.Content = new StackPanel();
tab.Items.Add(tabItem);
I am trying to add Tabs to a tab control in WPF but nothing appears on the control at runtime. I have tried following the examples I keep seeing. Right now this is what I have but it isn't working
_myConnection.Open();
SqlDataReader myReader = myCommand.ExecuteReader();
while (myReader.Read())
{
MessageBox.Show(myReader["SectionName"].ToString());
TabItem newTabItem = new TabItem
{
Header = myReader["SectionName"].ToString(),
Name = myReader["SectionID"].ToString()
};
TabMain.Items.Add(newTabItem);
}
_myConnection.Close();
TabMain.SelectedIndex = 0;
You can add tabs dynamically by using the following code.
Add the following code to declare tab control instance globally.
TabControl tbControl;
Now, add the following code to the loaded event of the tab control.
private void tbCtrl_Loaded(object sender, RoutedEventArgs e)
{
tbControl = (sender as TabControl);
}
I have used a button to add new tabs for the existing tab control.
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
TabItem newTabItem = new TabItem
{
Header = "Test",
Name = "Test"
};
tbControl.Items.Add(newTabItem);
}
Following is my tab control xaml view.
<TabControl x:Name="tbCtrl" HorizontalAlignment="Left" Height="270" Margin="54,36,0,0" VerticalAlignment="Top" Width="524" Loaded="tbCtrl_Loaded">
<TabItem Header="Tab - 01">
<Grid Background="#FFE5E5E5">
<Button x:Name="btnAdd" Content="Add New Tab" HorizontalAlignment="Left" Margin="68,95,0,0" VerticalAlignment="Top" Width="109" Height="29" Click="btnAdd_Click"/>
</Grid>
</TabItem>
</TabControl>
Finally, using this you can add any amount of tabs dynamically to the existing tab control.
Hope this fulfill your need.
Perhaps something in your DB values? I just wrote the most trivial of for loops to test, and this works fine (using just a TabControl and OnLoaded event on the XAML):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 1; i <= 3; i++)
{
var item = new TabItem {Header = i.ToString(), Name = $"tab{i}"};
TabMain.Items.Add(item);
}
}
I'm trying to remove items/rows from a ListView but the difficulty is that I need to also pass in some delegate or fire some event or something, so when a person clicks a button to remove that row, my code handles some other logic, elsewhere (eg. remove the item from the DB or whatever).
I have a custom control I made:
public class SportsTeam : StackLayout { .. }
Inside this control, one of the elements is a ListView which lists all the people in a sporting team.
var viewModel = teamMembers.Select(x => new SportsTeamViewModel(x));
return new ListView
{
HasUnevenRows = true,
ItemSource = viewModel,
ItemTemplate = new DataTemplate(typeof(SportsTeamViewCell));
};
Inside the SportsTeamViewCell I have the following:
private Grid CreateContent()
{
var grid = new Grid();
// Setup row and column definitions.
// Add items to the Grid
grid.Children.Add(...);
var removeButton = RemoveButton;
grid.Children.Add(removeButton);
Grid.SetRowSpan(removeButton, 2);
return grid;
}
private Button RemoveButton
{
get
{
var button = new Button
{
Image = "Icons/remove.png"
};
return button;
}
}
From here, I don't know how to make it so that the button fires an event or some delete could be passed in via the constructor, so some custom logic is performed against the individual cell/row/item that is to be removed.
Here is what you could do :
This be my model class :
public class Item
{
public string ItemName { get; set; }
public string ItemDetails { get; set; }
}
And in my XAML or you can write this in code as well, bind to the Command Parameter of your Item template :
<Button Text="Delete" CommandParameter="{Binding ItemName}" Clicked="DeleteClicked"></Button>
Full Item Template will be like below :
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding ItemName}" HorizontalOptions="StartAndExpand" FontSize="30"></Label>
<Button Text="Delete" CommandParameter="{Binding ItemName}" Clicked="DeleteClicked">
</Button>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
And in you code file you can do this :
public void DeleteClicked(object sender, EventArgs e)
{
var item = (Xamarin.Forms.Button)sender;
Item listitem = (from itm in allItems
where itm.ItemName == item.CommandParameter.ToString()
select itm)
.FirstOrDefault<Item>();
allItems.Remove(listitem);
}
IMPORTANT : This would only delete the item from the bound collection. To delete it from the original list you need to use ObservableCollection
Here is the full source code of the explained scenario - Handling Child Control Event in Listview using XAMARIN.FORMS.
Also the Tutorial - How to handle Row selection and delete Button in Row For Custom ListView using Xamarin.Forms explain deletion from a listview as well.
I've found a similar approach and I want to share it. I filled the list with an ObservableCollection<MyObject>. Then I filled the CommandParameter with just CommandParameter="{Binding .}". So I got the whole Object back. Then you can just cast the CommandParameterto your Object and remove it from the ObservableCollection<MyObject> List
XAML:
CommandParameter="{Binding .}"
Filling my List:
savingExpensesCollection = new ObservableCollection<SavingsExpensesEntry> ();
savingExpensesCollection .Add (new SavingsExpensesEntry ("1000 mAh Akku", "Dampfgarten", new DateTime (635808692400000000), 8.95));
savingExpensesCollection .Add (new SavingsExpensesEntry ("Cool-Mint Aroma", "Dampfgarten", new DateTime (635808692400000000), 3.95));
savingExpensesCollection .Add (new SavingsExpensesEntry ("Basis", "Dampfgarten", new DateTime (635808692400000000), 13.65));
savingExpensesList.ItemsSource = savingExpenses;
EventHandler:
void OnDelete(object sender, EventArgs e)
{
var menuItem = ((MenuItem)sender);
SavingsExpensesEntry see ((SavingsExpensesEntry)menuItem.CommandParameter);
savingExpensesCollection .Remove (see);
}
I've using a MenuItem but it's the same approach with a Button
I just did using delete button
public void OnDelete(object sender, EventArgs e)
{
var mi = ((MenuItem)sender);
PhotoViewModel photo= ((photoViewModel)mi.CommandParameter);
photoModel.Remove(photo);
}
I wonder how I can create buttons in my Toolbar by reading lines from a .txt file.
For example:
//bookmarks.txt
http://example.com
http://example2.com
http://example3.com
...
What I want is that my program on start should create a button for each line in my .txt with this event:
public void Button_Click(object sender, RoutedEventArgs e) //fire bookmark event
{
string text = e.Source.ToString().Replace("System.Windows.Controls.Button: ", "");
WebBrowser1.Navigate(text);
}
UPDATE
This is how I read the .txt:
for (int i = 0; i < File.ReadLines(#"bookmarks.txt").Count(); i++)
{
//Add button right here
}
You're trying to use WPF as if it were WinForms. This is how you would fulfil your requirements in WPF... first create a DependencyProperty collection in your Window code behind and populate it with your text entries:
public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(YourWindowOrUserControl));
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
...
Items = new ObservableCollection<string>(File.ReadLines(#"bookmarks.txt"));
Then you simply data bind the collection to the ToolBar.ItemsSource property and declare a DataTemplate to define what each string should look like... in your case, we'll set it as the text in a Button:
<ToolBar ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" Margin="1,0,0,0" />
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
Of course, you'll need to set the Window.DataContext to the class with your properties... the simplest way is to set it in the code behind constructor like this:
public YourWindowOrUserControl
{
InitializeComponent();
DataContext = this;
}
You must read up about how to set the DataContext properly though, as setting it this way is easy, but not necessarily correct.
Finally, you could create a class with all the necessary properties for the Button... for example, you could add a property named Text and another called Command and then make your Items property a collection of those. Then you could data bind to it like this:
<ToolBar ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Text}" Command="{Binding Command}" Margin="1,0,0,0" />
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
You can create buttons dynamic and add click event on fly:
Button btn = new Button();
btn.Location = new Point(yourX, yourY);
btn.Font = new Font(btn.Font.Name, 10);
btn.Text = "Text from your txt file here";
btn.ForeColor = Color.SeaShell; // choose color
btn.AutoSize = true;
btn.Click += (sender, eventArgs) =>
{
string text = btn.Text.Replace("System.Windows.Controls.Button: ", "");
WebBrowser1.Navigate(text);
};
(Insert this code in your For. Btw, you can replace the for with while. see this link)