Blazor 创建通用下拉菜单
Blazor creating a generic drop-down
我正在尝试创建一个通用下拉组件以在我们的系统中使用。但是,在更改所选项目时绑定 EventCallback
时遇到问题。
这是我目前对通用下拉菜单的想法:
<div class="inputItem @(SizeClass) dropdown" style="min-width:@(Width);">
<SfDropDownList TItem="object" TValue="int" Placeholder="Select a category" DataSource="DataSource" Value="@(SelectedItem)" EnableVirtualization="true">
<DropDownListEvents TItem="object" TValue="int" ValueChange="@OnSelectedItemChanged"></DropDownListEvents>
<DropDownListFieldSettings Text="@(TextField)" Value="@(ValueField)" />
</SfDropDownList>
</div>
@code {
[Parameter]
public IEnumerable<object> DataSource { get; set; }
[Parameter]
public EventCallback<ChangeEventArgs<int, object>> OnSelectedItemChanged { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string TextField { get; set; }
[Parameter]
public int SelectedItem { get; set; }
[Parameter]
public string ValueField { get; set; }
[Parameter]
public string Width { get; set; }
[Parameter]
public string SizeClass { get; set; }
}
下面是一个调用它的示例组件:
@page "/news/create"
@inject NavigationManager NavManager;
@using Microsoft.EntityFrameworkCore;
@inject IDbContextFactory<FIS2_DbContext> contextFactory;
@inject IFileService fileService;
@using FIS2withSyncfusion.Controls;
@using FIS2withSyncfusion.Models;
@using FIS2withSyncfusion.Utility;
@using Syncfusion.Blazor.RichTextEditor;
@using System.Collections.Generic;
@using System.Threading.Tasks;
@using Newtonsoft.Json;
<div class="dashWrapper">
<SfDashboardLayout AllowDragging="false" AllowFloating="false" AllowResizing="false" CellAspectRatio="2.5" CellSpacing="@(new double[]{20,20})" Columns="3">
<DashboardLayoutPanels>
<DashboardLayoutPanel Column="0" Row="0" SizeX="2" SizeY="2" Id="createNews">
<HeaderTemplate>
<h3>Create A News Item</h3>
</HeaderTemplate>
<ContentTemplate>
<div class="form-wrapper">
<div class="inputRow">
<TextBox AutoComplete="@(Syncfusion.Blazor.Inputs.AutoComplete.Off)" Placeholder="Title" Text="@(title)" HTMLAttributes="@textboxValidation" Width="450px" SizeClass="half-width"></TextBox>
<DropDownList DataSource="categories" Placeholder="Select a category" SizeClass="half-width" Width="450px" TextField="name" ValueField="id" SelectedItem="@(itemModel.Category)" OnSelectedItemChanged="@(OnSelectedItemChanged)"></DropDownList>
@*<SfDropDownList TItem="spGetNewsCategoriesResult" TValue="int" Placeholder="Select a category" @ref="sfDropDown" DataSource="categories" CssClass="inputItem half-width" @bind-Value="@(itemModel.Category)">
<DropDownListFieldSettings Text="name" Value="id" />
</SfDropDownList>*@
</div>
<div class="inputRow">
<CheckBox Checked="isChecked" Label="Suggest Dates This Should Be Active?" OnCheckChange="@(OnCheckChange)" SizeClass="one-third" Width="300px"></CheckBox>
@if (isChecked)
{
<DateTimePicker Label="Active From:" SelectedDate="@activeFrom" Width="450px" SizeClass="one-third"></DateTimePicker>
<DateTimePicker Label="Active To:" SelectedDate="@activeTo" Width="450px" SizeClass="one-third"></DateTimePicker>
}
</div>
<div class="inputRow">
<FileUploader MaxSize="@(MaxSize)" OnClearFiles="OnClearFiles" OnFileRemove="OnFileRemove" OnFileUpload="OnFileUpload" SizeClass="full-width" Width="400px"></FileUploader>
</div>
<RichTextEditor DeniedAttributes="@DeniedAttributes" text=@(itemModel.Content) Height="400px" Width="1600px"></RichTextEditor>
</div>
</ContentTemplate>
</DashboardLayoutPanel>
</DashboardLayoutPanels>
</SfDashboardLayout>
</div>
@if (ShowDialog)
{
<Dialog Title="Create News Item" message="@Message" OKText="@OKText" cancelText="@CancelText" OnClose="OnDialogClose">
</Dialog>
}
@code {
[CascadingParameter]
Task<AuthenticationState> authenticationStateTask { get; set; }
public string userName { get; set; }
private int MaxSize { get; set; }
private string title { get; set; }
private int selectedCategory { get; set; }
private string content { get; set; }
int count { get; set; }
private bool ShowDialog { get; set; } = false;
private string Message { get; set; } = "";
private string OKText { get; set; } = "";
private string CancelText { get; set; } = "";
public DateTime activeTo { get; set; }
public DateTime activeFrom { get; set; }
private bool isChecked { get; set; }
SaveNewsItemModel itemModel = new SaveNewsItemModel();
List<string> DeniedAttributes = new List<string>() {
"id", "title", "style"
};
Dictionary<string, object> textboxValidation = new Dictionary<string, object>(){
{"maxlength", "100"}
};
List<spGetNewsCategoriesResult> categories = new List<spGetNewsCategoriesResult>();
private async Task OnCheckChange(bool check)
{
isChecked = check;
StateHasChanged();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var authState = await authenticationStateTask;
var user = authState.User;
userName = user.Identity.Name;
var context = contextFactory.CreateDbContext();
var procedures = context.Procedures;
categories = await procedures.spGetNewsCategoriesAsync();
MaxSize = 15 * 1024 * 1024;
}
}
private List<ToolbarItemModel> Tools = new List<ToolbarItemModel>() {
new ToolbarItemModel()
{
Command = ToolbarCommand.Bold
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Italic
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Underline
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Undo
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Redo
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.OrderedList
},
new ToolbarItemModel()
{
Command = ToolbarCommand.UnorderedList
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.FontColor
},
new ToolbarItemModel()
{
Command = ToolbarCommand.CreateLink
},
new ToolbarItemModel()
{
Command = ToolbarCommand.RemoveLink
}
};
private async Task OnFileUpload(UploadChangeEventArgs args)
{
foreach (var file in args.Files)
{
var fileName = file.FileInfo.Name;
using (var ms = file.Stream)
{
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
int count = 1;
string tempFileName = fileName;
while (fileService.TempFileExists(tempFileName))
{
tempFileName = $"({count}) {fileName}";
count++;
}
var bytes = ms.ToArray();
await fileService.SaveFileToTempAsync(bytes, tempFileName);
var mimetype = fileInfo.Extension;
itemModel.AddFile(fileName, mimetype, tempFileName, contextFactory);
}
}
}
private async Task OnClearFiles(ClearingEventArgs args)
{
foreach (var file in args.FilesData)
{
var fileName = file.Name;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
itemModel.RemoveFile(fileName, fileInfo.Extension, contextFactory, fileService);
}
}
private async Task OnFileRemove(RemovingEventArgs args)
{
foreach (var file in args.FilesData)
{
var fileName = file.Name;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
itemModel.RemoveFile(fileName, fileInfo.Extension, contextFactory, fileService);
}
}
private async Task OnSelectedItemChanged(ChangeEventArgs<int, spGetNewsCategoriesResult> eventArgs)
{
itemModel.Category = eventArgs.Value;
StateHasChanged();
}
private async Task OnSave()
{
if (isChecked)
{
itemModel.RequestDates(activeFrom, activeTo);
}
var context = contextFactory.CreateDbContext();
var procedures = context.Procedures;
var addedFiles = await procedures.spCreateNewsItemAsync(JsonConvert.SerializeObject(itemModel), userName);
if (addedFiles.Count > 0)
{
foreach (var file in addedFiles)
{
await fileService.MoveTempToNewsAsync(file.fileName, file.newsID, file.fileID);
}
}
Message = "This has been successfully saved and is now pending review; pressing OK will refresh the page.";
OKText = "OK";
ShowDialog = true;
}
private async Task OnDialogClose(bool r)
{
ShowDialog = false;
NavManager.NavigateTo(NavManager.Uri, true);
}
}
我的问题是此时出现错误:OnSelectedItemChanged="@(OnSelectedItemChanged)"
错误是:
Cannot convert from method group
to EventCallback
我所做的搜索似乎暗示我需要明确地将类型作为参数传递,而不是使用 object
并尝试在运行时推断它 - 我只是有点糊涂关于具体怎么做?
TValue
成为 int
是任何地方都不应该改变的。但是 TItem
几乎可以是任何东西(在这个特定的场景中它是 spGetNewsCategoriesResult
)——我该如何满足它?
经过大量搜索和修改,我找到了解决方案。通过将组件更改为:
@typeparam T
<div class="inputItem @(SizeClass) dropdown" style="min-width:@(Width);">
<SfDropDownList TItem="T" TValue="int" Placeholder="Select a category" DataSource="DataSource" Value="@(SelectedItem)" EnableVirtualization="true">
<DropDownListEvents TItem="T" TValue="int" ValueChange="@OnSelectedItemChanged"></DropDownListEvents>
<DropDownListFieldSettings Text="@(TextField)" Value="@(ValueField)" />
</SfDropDownList>
</div>
@code {
[Parameter]
public IEnumerable<T> DataSource { get; set; }
[Parameter]
public EventCallback<Syncfusion.Blazor.DropDowns.ChangeEventArgs<int, T>> OnSelectedItemChanged { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string TextField { get; set; }
[Parameter]
public int SelectedItem { get; set; }
[Parameter]
public string ValueField { get; set; }
[Parameter]
public string Width { get; set; }
[Parameter]
public string SizeClass { get; set; }
}
并这样引用它:
<DropDownList DataSource="categories" Placeholder="Select a category" SizeClass="half-width" Width="450px" TextField="name" ValueField="id" SelectedItem="@(itemModel.Category)" OnSelectedItemChanged="@(OnSelectedItemChanged)" T="spGetNewsCategoriesResult"></DropDownList>
错误已解决。决定回答我自己的问题,而不是仅仅删除它,因为我认为它可能会在人们自己搜索时弹出。
我正在尝试创建一个通用下拉组件以在我们的系统中使用。但是,在更改所选项目时绑定 EventCallback
时遇到问题。
这是我目前对通用下拉菜单的想法:
<div class="inputItem @(SizeClass) dropdown" style="min-width:@(Width);">
<SfDropDownList TItem="object" TValue="int" Placeholder="Select a category" DataSource="DataSource" Value="@(SelectedItem)" EnableVirtualization="true">
<DropDownListEvents TItem="object" TValue="int" ValueChange="@OnSelectedItemChanged"></DropDownListEvents>
<DropDownListFieldSettings Text="@(TextField)" Value="@(ValueField)" />
</SfDropDownList>
</div>
@code {
[Parameter]
public IEnumerable<object> DataSource { get; set; }
[Parameter]
public EventCallback<ChangeEventArgs<int, object>> OnSelectedItemChanged { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string TextField { get; set; }
[Parameter]
public int SelectedItem { get; set; }
[Parameter]
public string ValueField { get; set; }
[Parameter]
public string Width { get; set; }
[Parameter]
public string SizeClass { get; set; }
}
下面是一个调用它的示例组件:
@page "/news/create"
@inject NavigationManager NavManager;
@using Microsoft.EntityFrameworkCore;
@inject IDbContextFactory<FIS2_DbContext> contextFactory;
@inject IFileService fileService;
@using FIS2withSyncfusion.Controls;
@using FIS2withSyncfusion.Models;
@using FIS2withSyncfusion.Utility;
@using Syncfusion.Blazor.RichTextEditor;
@using System.Collections.Generic;
@using System.Threading.Tasks;
@using Newtonsoft.Json;
<div class="dashWrapper">
<SfDashboardLayout AllowDragging="false" AllowFloating="false" AllowResizing="false" CellAspectRatio="2.5" CellSpacing="@(new double[]{20,20})" Columns="3">
<DashboardLayoutPanels>
<DashboardLayoutPanel Column="0" Row="0" SizeX="2" SizeY="2" Id="createNews">
<HeaderTemplate>
<h3>Create A News Item</h3>
</HeaderTemplate>
<ContentTemplate>
<div class="form-wrapper">
<div class="inputRow">
<TextBox AutoComplete="@(Syncfusion.Blazor.Inputs.AutoComplete.Off)" Placeholder="Title" Text="@(title)" HTMLAttributes="@textboxValidation" Width="450px" SizeClass="half-width"></TextBox>
<DropDownList DataSource="categories" Placeholder="Select a category" SizeClass="half-width" Width="450px" TextField="name" ValueField="id" SelectedItem="@(itemModel.Category)" OnSelectedItemChanged="@(OnSelectedItemChanged)"></DropDownList>
@*<SfDropDownList TItem="spGetNewsCategoriesResult" TValue="int" Placeholder="Select a category" @ref="sfDropDown" DataSource="categories" CssClass="inputItem half-width" @bind-Value="@(itemModel.Category)">
<DropDownListFieldSettings Text="name" Value="id" />
</SfDropDownList>*@
</div>
<div class="inputRow">
<CheckBox Checked="isChecked" Label="Suggest Dates This Should Be Active?" OnCheckChange="@(OnCheckChange)" SizeClass="one-third" Width="300px"></CheckBox>
@if (isChecked)
{
<DateTimePicker Label="Active From:" SelectedDate="@activeFrom" Width="450px" SizeClass="one-third"></DateTimePicker>
<DateTimePicker Label="Active To:" SelectedDate="@activeTo" Width="450px" SizeClass="one-third"></DateTimePicker>
}
</div>
<div class="inputRow">
<FileUploader MaxSize="@(MaxSize)" OnClearFiles="OnClearFiles" OnFileRemove="OnFileRemove" OnFileUpload="OnFileUpload" SizeClass="full-width" Width="400px"></FileUploader>
</div>
<RichTextEditor DeniedAttributes="@DeniedAttributes" text=@(itemModel.Content) Height="400px" Width="1600px"></RichTextEditor>
</div>
</ContentTemplate>
</DashboardLayoutPanel>
</DashboardLayoutPanels>
</SfDashboardLayout>
</div>
@if (ShowDialog)
{
<Dialog Title="Create News Item" message="@Message" OKText="@OKText" cancelText="@CancelText" OnClose="OnDialogClose">
</Dialog>
}
@code {
[CascadingParameter]
Task<AuthenticationState> authenticationStateTask { get; set; }
public string userName { get; set; }
private int MaxSize { get; set; }
private string title { get; set; }
private int selectedCategory { get; set; }
private string content { get; set; }
int count { get; set; }
private bool ShowDialog { get; set; } = false;
private string Message { get; set; } = "";
private string OKText { get; set; } = "";
private string CancelText { get; set; } = "";
public DateTime activeTo { get; set; }
public DateTime activeFrom { get; set; }
private bool isChecked { get; set; }
SaveNewsItemModel itemModel = new SaveNewsItemModel();
List<string> DeniedAttributes = new List<string>() {
"id", "title", "style"
};
Dictionary<string, object> textboxValidation = new Dictionary<string, object>(){
{"maxlength", "100"}
};
List<spGetNewsCategoriesResult> categories = new List<spGetNewsCategoriesResult>();
private async Task OnCheckChange(bool check)
{
isChecked = check;
StateHasChanged();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var authState = await authenticationStateTask;
var user = authState.User;
userName = user.Identity.Name;
var context = contextFactory.CreateDbContext();
var procedures = context.Procedures;
categories = await procedures.spGetNewsCategoriesAsync();
MaxSize = 15 * 1024 * 1024;
}
}
private List<ToolbarItemModel> Tools = new List<ToolbarItemModel>() {
new ToolbarItemModel()
{
Command = ToolbarCommand.Bold
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Italic
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Underline
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Undo
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Redo
},
new ToolbarItemModel()
{
Command= ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.OrderedList
},
new ToolbarItemModel()
{
Command = ToolbarCommand.UnorderedList
},
new ToolbarItemModel()
{
Command = ToolbarCommand.Separator
},
new ToolbarItemModel()
{
Command = ToolbarCommand.FontColor
},
new ToolbarItemModel()
{
Command = ToolbarCommand.CreateLink
},
new ToolbarItemModel()
{
Command = ToolbarCommand.RemoveLink
}
};
private async Task OnFileUpload(UploadChangeEventArgs args)
{
foreach (var file in args.Files)
{
var fileName = file.FileInfo.Name;
using (var ms = file.Stream)
{
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
int count = 1;
string tempFileName = fileName;
while (fileService.TempFileExists(tempFileName))
{
tempFileName = $"({count}) {fileName}";
count++;
}
var bytes = ms.ToArray();
await fileService.SaveFileToTempAsync(bytes, tempFileName);
var mimetype = fileInfo.Extension;
itemModel.AddFile(fileName, mimetype, tempFileName, contextFactory);
}
}
}
private async Task OnClearFiles(ClearingEventArgs args)
{
foreach (var file in args.FilesData)
{
var fileName = file.Name;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
itemModel.RemoveFile(fileName, fileInfo.Extension, contextFactory, fileService);
}
}
private async Task OnFileRemove(RemovingEventArgs args)
{
foreach (var file in args.FilesData)
{
var fileName = file.Name;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
itemModel.RemoveFile(fileName, fileInfo.Extension, contextFactory, fileService);
}
}
private async Task OnSelectedItemChanged(ChangeEventArgs<int, spGetNewsCategoriesResult> eventArgs)
{
itemModel.Category = eventArgs.Value;
StateHasChanged();
}
private async Task OnSave()
{
if (isChecked)
{
itemModel.RequestDates(activeFrom, activeTo);
}
var context = contextFactory.CreateDbContext();
var procedures = context.Procedures;
var addedFiles = await procedures.spCreateNewsItemAsync(JsonConvert.SerializeObject(itemModel), userName);
if (addedFiles.Count > 0)
{
foreach (var file in addedFiles)
{
await fileService.MoveTempToNewsAsync(file.fileName, file.newsID, file.fileID);
}
}
Message = "This has been successfully saved and is now pending review; pressing OK will refresh the page.";
OKText = "OK";
ShowDialog = true;
}
private async Task OnDialogClose(bool r)
{
ShowDialog = false;
NavManager.NavigateTo(NavManager.Uri, true);
}
}
我的问题是此时出现错误:OnSelectedItemChanged="@(OnSelectedItemChanged)"
错误是:
Cannot convert from
method group
toEventCallback
我所做的搜索似乎暗示我需要明确地将类型作为参数传递,而不是使用 object
并尝试在运行时推断它 - 我只是有点糊涂关于具体怎么做?
TValue
成为 int
是任何地方都不应该改变的。但是 TItem
几乎可以是任何东西(在这个特定的场景中它是 spGetNewsCategoriesResult
)——我该如何满足它?
经过大量搜索和修改,我找到了解决方案。通过将组件更改为:
@typeparam T
<div class="inputItem @(SizeClass) dropdown" style="min-width:@(Width);">
<SfDropDownList TItem="T" TValue="int" Placeholder="Select a category" DataSource="DataSource" Value="@(SelectedItem)" EnableVirtualization="true">
<DropDownListEvents TItem="T" TValue="int" ValueChange="@OnSelectedItemChanged"></DropDownListEvents>
<DropDownListFieldSettings Text="@(TextField)" Value="@(ValueField)" />
</SfDropDownList>
</div>
@code {
[Parameter]
public IEnumerable<T> DataSource { get; set; }
[Parameter]
public EventCallback<Syncfusion.Blazor.DropDowns.ChangeEventArgs<int, T>> OnSelectedItemChanged { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string TextField { get; set; }
[Parameter]
public int SelectedItem { get; set; }
[Parameter]
public string ValueField { get; set; }
[Parameter]
public string Width { get; set; }
[Parameter]
public string SizeClass { get; set; }
}
并这样引用它:
<DropDownList DataSource="categories" Placeholder="Select a category" SizeClass="half-width" Width="450px" TextField="name" ValueField="id" SelectedItem="@(itemModel.Category)" OnSelectedItemChanged="@(OnSelectedItemChanged)" T="spGetNewsCategoriesResult"></DropDownList>
错误已解决。决定回答我自己的问题,而不是仅仅删除它,因为我认为它可能会在人们自己搜索时弹出。