Closed tlemmons closed 4 years ago
Sure, here is an example. I am also working on making improvements so that this is easier, but for now this will do for your case:
<ContextMenu Id="tableContextMenu">
<Item OnClick="@PlaylistClick" OnAppearing="OnAppearing"> <i class="@AddRemoveIcon"></i> @AddRemoveText</Item>
</ContextMenu>
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Other Stuff</th>
</tr>
</thead>
<tbody>
@foreach (var song in songs)
{
<ContextMenuTrigger WrapperTag="tr" MenuId="tableContextMenu" Data="song">
<td>@song.Title</td>
<td>@song.SomeOtherStuff</td>
</ContextMenuTrigger>
}
</tbody>
</table>
@code{
private string AddRemoveIcon;
private string AddRemoveText;
void PlaylistClick(ItemClickEventArgs e)
{
e.IsCanceled = true;
var song= e.Data as Song;
song.Added = !song.Added;
SetMenuItemText(song);
}
void OnAppearing(ItemAppearingEventArgs e)
{
var song= e.Data as Song;
SetMenuItemText(song);
}
void SetMenuItemText(Song song)
{
if (song.Added)
{
AddRemoveIcon = "far fa-square";
AddRemoveText = "Remove from playlist";
}
else
{
AddRemoveIcon = "far fa-check-square";
AddRemoveText = "Add to playlist";
}
}
}
Let me know if this covers your case
Version 1.6 simplifies the above example significantly
<ContextMenu Id="tableContextMenu">
@{
var song = context.Data as Song;
var iconClass = song.Added ? "far fa-square" : "far fa-check-square";
var text = song.Added ? "Remove from playlist" : "Add to playlist";
<Item OnClick="@PlaylistClick"> <i class="@iconClass"></i> @text</Item>
}
</ContextMenu>
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Other Stuff</th>
</tr>
</thead>
<tbody>
@foreach (var song in songs)
{
<ContextMenuTrigger WrapperTag="tr" MenuId="tableContextMenu" Data="song">
<td>@song.Title</td>
<td>@song.SomeOtherStuff</td>
</ContextMenuTrigger>
}
</tbody>
</table>
@code{
void PlaylistClick(ItemClickEventArgs e)
{
e.IsCanceled = true;
var song= e.Data as Song;
song.Added = !song.Added;
}
}
Closing assuming the provided solution works. Reopen if it doesnt
Sorry I have just gotten back from holiday travel. And I cannot figure out how to reopen so hoping another comment is enough.
I tried your example on 1.6 and am having problems with creating submenu items. My code is included below. The item for the submenu is created in the main menu but the submenu is not filled out. The code that should create multiple submenu items never get run. I am pretty new to these components. I have verified that the collection list gets created and populated during the onInitialized but the foreach loop in the submenu layout does not ever get called. Any help appreciated.
<ContextMenu` Id="myMenu">
<Item OnClick="@OnClick">Edit</Item>
<Item OnClick="@OnClick">Delete</Item>
<Seperator />
<Item>
Collections
<SubMenu>
@{
foreach (collectionData c in collections)
{
var name = c.Name;
var iconClass = c.selected ? "far fa-square" : "far fa-check-square";
<Item OnClick="@PlaylistClick"> <i class="@iconClass"></i> @name</Item>
}
}
</SubMenu>
</Item>
</ContextMenu>
<ContextMenuTrigger MenuId="myMenu">
<img src="https://www.nimbusframe.net/getImage.ashx?Id=@ID&height=150&type=1&crop=0" />
</ContextMenuTrigger>
@code {
[CascadingParameter] protected Users currentUser { get; set; }
[Parameter]
public string ID { get; set; }
public struct collectionData
{
public String Name;
public bool selected;
}
List<collectionData> collections = new List<collectionData>();
protected override void OnInitialized()
{
nimbusContext nc = new nimbusContext();
var cols = nc.Collections.Where(collection => collection.UId == currentUser.Id);
collections.Clear();
foreach (Collections c in cols)
{
collectionData tmp = new collectionData();
tmp.Name = c.Name;
tmp.selected = c.isImageIncluded(Int32.Parse(ID));
collections.Add(tmp);
}
}
void OnClick(ItemClickEventArgs e)
{
Console.WriteLine($"Item Clicked => Menu: {e.ContextMenuId}, MenuTarget: {e.ContextMenuTargetId}, IsCanceled: {e.IsCanceled}, MenuItem: {e.MenuItemElement}, MouseEvent: {e.MouseEvent}");
}
void PlaylistClick(ItemClickEventArgs e)
{
Console.WriteLine($"Item Clicked => Menu: {e.ContextMenuId}, MenuTarget: {e.ContextMenuTargetId}, IsCanceled: {e.IsCanceled}, MenuItem: {e.MenuItemElement}, MouseEvent: {e.MouseEvent}");
}
}
I will investigate this asap. The code is seemingly correct. I will get back to you on this
I managed to produce a working sample based on your code. I had to change collectionData
from a struct to a class, so that it get passed as a reference and also I had to fake the data obviously.
<ContextMenu Id="myMenu">
<Item OnClick="@OnClick">Edit</Item>
<Item OnClick="@OnClick">Delete</Item>
<Seperator />
<Item>
Collections
<SubMenu>
@{
foreach (var c in collections)
{
var iconClass = c.selected ? "far fa-check-square" : "far fa-square";
<Item OnClick="@((e)=> PlaylistClick(e,c))"> <i class="@iconClass"></i> @c.Name</Item>
}
}
</SubMenu>
</Item>
</ContextMenu>
<ContextMenuTrigger MenuId="myMenu">
Open menu
</ContextMenuTrigger>
@code {
public class collectionData
{
public String Name { get; set; }
public bool selected { get; set; }
}
List<collectionData> collections = new List<collectionData>();
protected override void OnInitialized()
{
for(int i=0; i < 5; i++)
{
var tmp = new collectionData();
tmp.Name = $"Collection {i+ 1}";
tmp.selected = false;
collections.Add(tmp);
}
}
void OnClick(ItemClickEventArgs e)
{
Console.WriteLine($"Item Clicked => Menu: {e.ContextMenuId}, MenuTarget: {e.ContextMenuTargetId}, IsCanceled: {e.IsCanceled}, MenuItem: {e.MenuItemElement}, MouseEvent: {e.MouseEvent}");
}
void PlaylistClick(ItemClickEventArgs e, collectionData collectionData)
{
e.IsCanceled = true;
collectionData.selected = !collectionData.selected;
}
}
This works, maybe the problem was the struct or there is some other error with your code ?
I took your code as is and replaced mine, It still does not create the submenu items. Could we have something else different in our setup? I am on the latest nuget package dated Friday, December 27, 2019.
My code above this does create many of these components, could multiple on a page be a problem?
Tested and if I only do one item with a context menu, it works. Multiple menus seems to cause all submenu items to not be shown. I need multiple different submenus since the checked items will be unique. Any ideas?
I cannot reproduce what you are describing. However I think that there is some misunderstanding on how the component works. Here is a list of things/facts to check:
<ContextMenu>
components in a page, just make sure that they have different ids.ContextMenu
does not have to be on the same page/component, it can be anywhere inside your App
, because it is being looked up by id, so as long as it is rendered somewhere, it will show up.<ContextMenu>
for the same content with one or more ContextMenuTriggers
.ContextMenuTrigger
's Data
parameter and the @context
variable in the ContextMenu
, and using the @context.Data
property your code can take decisions depending on the passed data.
Here is the example reworked, if my wall of text does not make sense:<ContextMenu Id="myMenu">
@if (((MenuType)context.Data) == MenuType.Type1)
{
<Item OnClick="@OnClick">Menu type 1</Item>
<Seperator />
<Item>
Collections
<SubMenu>
@{
foreach (var c in collections)
{
var iconClass = c.selected ? "far fa-check-square" : "far fa-square";
<Item OnClick="@((e) => PlaylistClick(e, c))"> <i class="@iconClass"></i> @c.Name</Item>
}
}
</SubMenu>
</Item>
}
else
{
<Item OnClick="@OnClick">Menu type 2</Item>
<Seperator />
}
</ContextMenu>
<ContextMenuTrigger MenuId="myMenu" Data="MenuType.Type1">
Open menu type 1
</ContextMenuTrigger>
<ContextMenuTrigger MenuId="myMenu" Data="MenuType.Type2">
Open menu type 2
</ContextMenuTrigger>
@code {
enum MenuType
{
Type1,
Type2
}
public class collectionData
{
public String Name { get; set; }
public bool selected { get; set; }
}
List<collectionData> collections = new List<collectionData>();
protected override void OnInitialized()
{
for (int i = 0; i < 5; i++)
{
var tmp = new collectionData();
tmp.Name = $"Collection {i + 1}";
tmp.selected = false;
collections.Add(tmp);
}
}
void OnClick(ItemClickEventArgs e)
{
Console.WriteLine($"Item Clicked => Menu: {e.ContextMenuId}, MenuTarget: {e.ContextMenuTargetId}, IsCanceled: {e.IsCanceled}, MenuItem: {e.MenuItemElement}, MouseEvent: {e.MouseEvent}");
}
void PlaylistClick(ItemClickEventArgs e, collectionData collectionData)
{
e.IsCanceled = true;
collectionData.selected = !collectionData.selected;
}
}
Am I getting closer to what you want to achieve ?
I think the problem is that I have the same menu Id for each one on the overall page. I need to figure out a way to dynamically create the Ids before the menu is generated. I am trying to figure that out now.
Why not create a single context menu, outside of your loop, and pass data to it from the trigger, so that the content of the menu is dynamic? This is what I recommend doing.
That makes sense. Are you talking about doing the same thing you did here with dynamic items, https://blazor-context-menu-demo.azurewebsites.net/dynamicitems by using onAppearing or is there a way to do it from the actual trigger event so it loops/sets all of them at once.
Yes, you can use the OnAppearing
event or use the @context.Data
showcased in the above examples. It is the same thing, since the ContextMenu
does not render its contents until shown.
Thanks for all the help. I think I have it working well.
Is there a way to have a menu item show a check or check box. I would like to be able to turn the check on and off without actually closing the menu. This would be used for adding/removing a song on several playlists.