Progress Telerik Ultimate Collection 2025 Q2下载地址 https://soft51.cc/software/175792580241152290
数据绑定是现代UI框架的核心功能,它实现了数据与界面的自动同步。Telerik UI支持多种数据绑定方式,能够无缝集成各种数据源。
静态数据绑定:
动态数据绑定:
双向数据绑定:
简单集合绑定:
@{
var products = new[]
{
new { ID = 1, Name = "笔记本电脑", Price = 5999.99 },
new { ID = 2, Name = "无线鼠标", Price = 199.99 },
new { ID = 3, Name = "机械键盘", Price = 699.99 }
};
}
@(Html.Kendo().Grid<object>()
.Name("productGrid")
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => {
model.Id("ID");
model.Field("ID", typeof(int));
model.Field("Name", typeof(string));
model.Field("Price", typeof(decimal));
})
.Read(read => read.Action("GetProducts", "Home"))
)
.Columns(columns =>
{
columns.Bound("ID").Width(80);
columns.Bound("Name").Title("产品名称").Width(200);
columns.Bound("Price").Title("价格").Format("{0:C}").Width(150);
})
)
强类型模型绑定:
// 模型定义
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
public DateTime CreatedDate { get; set; }
}
@(Html.Kendo().Grid<Product>()
.Name("typedGrid")
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(p => p.ID))
.Read(read => read.Action("GetProducts", "Product"))
)
.Columns(columns =>
{
columns.Bound(p => p.ID).Width(80);
columns.Bound(p => p.Name).Title("产品名称");
columns.Bound(p => p.Price).Title("价格").Format("{0:C}");
columns.Bound(p => p.Category).Title("分类");
})
)
<!-- 静态数据绑定 -->
@(Html.Kendo().DropDownList()
.Name("categoryList")
.BindTo(new SelectList(new[]
{
new { Value = "electronics", Text = "电子产品" },
new { Value = "clothing", Text = "服装" },
new { Value = "books", Text = "图书" }
}, "Value", "Text"))
.OptionLabel("请选择分类")
)
<!-- 动态数据绑定 -->
@(Html.Kendo().DropDownList()
.Name("dynamicCategoryList")
.DataTextField("Name")
.DataValueField("ID")
.DataSource(source => source
.Read(read => read.Action("GetCategories", "Product"))
)
.OptionLabel("加载中...")
)
DbContext 配置:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
// 服务注册
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
控制器实现:
[HttpGet]
public async Task<IActionResult> GetProducts([DataSourceRequest] DataSourceRequest request)
{
using var context = new ApplicationDbContext();
var products = context.Products.Include(p => p.Category);
var result = await products.ToDataSourceResultAsync(request);
return Json(result);
}
[HttpPost]
public async Task<IActionResult> CreateProduct([DataSourceRequest] DataSourceRequest request, Product product)
{
if (ModelState.IsValid)
{
using var context = new ApplicationDbContext();
context.Products.Add(product);
await context.SaveChangesAsync();
}
return Json(new[] { product }.ToDataSourceResult(request, ModelState));
}
[HttpPost]
public async Task<IActionResult> UpdateProduct([DataSourceRequest] DataSourceRequest request, Product product)
{
if (ModelState.IsValid)
{
using var context = new ApplicationDbContext();
context.Entry(product).State = EntityState.Modified;
await context.SaveChangesAsync();
}
return Json(new[] { product }.ToDataSourceResult(request, ModelState));
}
[HttpPost]
public async Task<IActionResult> DeleteProduct([DataSourceRequest] DataSourceRequest request, Product product)
{
using var context = new ApplicationDbContext();
var entity = context.Products.Find(product.ID);
if (entity != null)
{
context.Products.Remove(entity);
await context.SaveChangesAsync();
}
return Json(new[] { product }.ToDataSourceResult(request, ModelState));
}
Grid CRUD 配置:
@(Html.Kendo().Grid<Product>()
.Name("crudGrid")
.Columns(columns =>
{
columns.Bound(p => p.Name).Title("产品名称");
columns.Bound(p => p.Price).Title("价格").Format("{0:C}");
columns.Bound(p => p.Category).Title("分类");
columns.Command(command =>
{
command.Edit();
command.Destroy();
}).Width(200);
})
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(10)
.Model(model =>
{
model.Id(p => p.ID);
model.Field(p => p.ID).Editable(false);
})
.Create(create => create.Action("CreateProduct", "Product"))
.Read(read => read.Action("GetProducts", "Product"))
.Update(update => update.Action("UpdateProduct", "Product"))
.Destroy(destroy => destroy.Action("DeleteProduct", "Product"))
)
.Pageable()
.Sortable()
.Filterable()
)
API 控制器:
[ApiController]
[Route("api/[controller]")]
public class ProductsApiController : ControllerBase
{
private readonly IProductService _productService;
public ProductsApiController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
public async Task<IActionResult> Get([DataSourceRequest] DataSourceRequest request)
{
var products = await _productService.GetAllAsync();
var result = products.ToDataSourceResult(request);
return Ok(result);
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] Product product)
{
if (ModelState.IsValid)
{
await _productService.CreateAsync(product);
return Ok(product);
}
return BadRequest(ModelState);
}
[HttpPut("{id}")]
public async Task<IActionResult> Put(int id, [FromBody] Product product)
{
if (id != product.ID)
return BadRequest();
if (ModelState.IsValid)
{
await _productService.UpdateAsync(product);
return Ok(product);
}
return BadRequest(ModelState);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
await _productService.DeleteAsync(id);
return Ok();
}
}
客户端 API 绑定:
@(Html.Kendo().Grid<Product>()
.Name("apiGrid")
.DataSource(dataSource => dataSource
.Ajax()
.Transport(transport =>
{
transport.Read(read => read.Url("/api/products").Type(HttpVerbs.Get));
transport.Create(create => create.Url("/api/products").Type(HttpVerbs.Post));
transport.Update(update => update.Url("/api/products/{0}").Type(HttpVerbs.Put));
transport.Destroy(destroy => destroy.Url("/api/products/{0}").Type(HttpVerbs.Delete));
transport.ParameterMap(@<text>
function(options, operation) {
if (operation !== "read" && options.models) {
return JSON.stringify(options.models[0]);
}
return options;
}
</text>);
})
.Schema(schema =>
{
schema.Model(model =>
{
model.Id(p => p.ID);
model.Field(p => p.ID).Editable(false);
});
schema.Data("Data");
schema.Total("Total");
})
.ServerOperation(false)
)
)
public class ExternalApiService
{
private readonly HttpClient _httpClient;
public ExternalApiService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<List<Product>> GetProductsFromExternalApiAsync()
{
var response = await _httpClient.GetStringAsync("https://api.example.com/products");
var products = JsonSerializer.Deserialize<List<Product>>(response);
return products;
}
}
// 控制器使用
[HttpGet]
public async Task<IActionResult> GetExternalProducts([DataSourceRequest] DataSourceRequest request)
{
var products = await _externalApiService.GetProductsFromExternalApiAsync();
var result = products.ToDataSourceResult(request);
return Json(result);
}
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
}
public class ProductViewModel : ViewModelBase
{
private ObservableCollection<Product> _products;
private Product _selectedProduct;
private string _searchText;
public ProductViewModel()
{
Products = new ObservableCollection<Product>();
LoadProductsCommand = new RelayCommand(async () => await LoadProducts());
AddProductCommand = new RelayCommand(AddProduct);
DeleteProductCommand = new RelayCommand(DeleteProduct, () => SelectedProduct != null);
SearchCommand = new RelayCommand(SearchProducts);
LoadProducts();
}
public ObservableCollection<Product> Products
{
get => _products;
set => SetProperty(ref _products, value);
}
public Product SelectedProduct
{
get => _selectedProduct;
set
{
SetProperty(ref _selectedProduct, value);
DeleteProductCommand.RaiseCanExecuteChanged();
}
}
public string SearchText
{
get => _searchText;
set => SetProperty(ref _searchText, value);
}
public ICommand LoadProductsCommand { get; }
public ICommand AddProductCommand { get; }
public ICommand DeleteProductCommand { get; }
public ICommand SearchCommand { get; }
private async Task LoadProducts()
{
try
{
var products = await _productService.GetAllAsync();
Products.Clear();
foreach (var product in products)
{
Products.Add(product);
}
}
catch (Exception ex)
{
// 处理错误
MessageBox.Show($"加载产品失败: {ex.Message}");
}
}
private void AddProduct()
{
var newProduct = new Product
{
Name = "新产品",
Price = 0,
Category = "未分类"
};
Products.Add(newProduct);
SelectedProduct = newProduct;
}
private async void DeleteProduct()
{
if (SelectedProduct == null) return;
var result = MessageBox.Show(
$"确定要删除产品 '{SelectedProduct.Name}' 吗?",
"确认删除",
MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
try
{
await _productService.DeleteAsync(SelectedProduct.ID);
Products.Remove(SelectedProduct);
}
catch (Exception ex)
{
MessageBox.Show($"删除失败: {ex.Message}");
}
}
}
private void SearchProducts()
{
// 实现搜索逻辑
if (string.IsNullOrWhiteSpace(SearchText))
{
LoadProducts();
return;
}
var filteredProducts = Products
.Where(p => p.Name.Contains(SearchText, StringComparison.OrdinalIgnoreCase))
.ToList();
Products.Clear();
foreach (var product in filteredProducts)
{
Products.Add(product);
}
}
}
<Window x:Class="TelerikApp.Views.ProductWindow"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
DataContext="{Binding ProductViewModel, Source={StaticResource ViewModelLocator}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 搜索栏 -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10">
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
Width="200" Height="30" Margin="0,0,10,0"/>
<telerik:RadButton Content="搜索"
Command="{Binding SearchCommand}"
Width="80" Height="30"/>
</StackPanel>
<!-- 产品列表 -->
<telerik:RadDataGrid Grid.Row="1"
ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct}"
AutoGenerateColumns="False"
Margin="10">
<telerik:RadDataGrid.Columns>
<telerik:DataGridTextColumn PropertyName="ID"
Header="ID" Width="80"/>
<telerik:DataGridTextColumn PropertyName="Name"
Header="产品名称" Width="*"/>
<telerik:DataGridTextColumn PropertyName="Price"
Header="价格"
CellContentFormat="{}{0:C}"/>
<telerik:DataGridTextColumn PropertyName="Category"
Header="分类"/>
</telerik:RadDataGrid.Columns>
</telerik:RadDataGrid>
<!-- 操作按钮 -->
<StackPanel Grid.Row="2" Orientation="Horizontal"
HorizontalAlignment="Right" Margin="10">
<telerik:RadButton Content="刷新"
Command="{Binding LoadProductsCommand}"
Width="80" Height="35" Margin="0,0,10,0"/>
<telerik:RadButton Content="添加"
Command="{Binding AddProductCommand}"
Width="80" Height="35" Margin="0,0,10,0"/>
<telerik:RadButton Content="删除"
Command="{Binding DeleteProductCommand}"
Width="80" Height="35"/>
</StackPanel>
</Grid>
</Window>
public class ProductController : Controller
{
private readonly IProductService _productService;
private readonly IMapper _mapper;
public ProductController(IProductService productService, IMapper mapper)
{
_productService = productService;
_mapper = mapper;
}
public async Task<IActionResult> Index()
{
var products = await _productService.GetAllAsync();
var viewModel = new ProductIndexViewModel
{
Products = products,
Categories = await _productService.GetCategoriesAsync()
};
return View(viewModel);
}
[HttpPost]
public async Task<IActionResult> GetProducts([DataSourceRequest] DataSourceRequest request)
{
try
{
var products = await _productService.GetAllAsync();
var result = products.ToDataSourceResult(request);
return Json(result);
}
catch (Exception ex)
{
return Json(new DataSourceResult
{
Errors = ex.Message
});
}
}
[HttpPost]
public async Task<IActionResult> CreateProduct([DataSourceRequest] DataSourceRequest request,
ProductCreateDto dto)
{
if (ModelState.IsValid)
{
try
{
var product = _mapper.Map<Product>(dto);
await _productService.CreateAsync(product);
var resultDto = _mapper.Map<ProductDto>(product);
return Json(new[] { resultDto }.ToDataSourceResult(request, ModelState));
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
}
}
return Json(new[] { dto }.ToDataSourceResult(request, ModelState));
}
}
public class ProductIndexViewModel
{
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Category> Categories { get; set; }
public ProductFilterModel Filter { get; set; } = new();
}
public class ProductFilterModel
{
public string Name { get; set; }
public string Category { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
public class ProductCreateDto
{
[Required(ErrorMessage = "产品名称不能为空")]
public string Name { get; set; }
[Required(ErrorMessage = "价格不能为空")]
[Range(0.01, double.MaxValue, ErrorMessage = "价格必须大于0")]
public decimal Price { get; set; }
[Required(ErrorMessage = "分类不能为空")]
public string Category { get; set; }
public string Description { get; set; }
}
public interface IProductService
{
Task<IEnumerable<Product>> GetAllAsync();
Task<Product> GetByIdAsync(int id);
Task<Product> CreateAsync(Product product);
Task<Product> UpdateAsync(Product product);
Task<bool> DeleteAsync(int id);
Task<IEnumerable<Category>> GetCategoriesAsync();
}
public class ProductService : IProductService
{
private readonly ApplicationDbContext _context;
public ProductService(ApplicationDbContext context)
{
_context = context;
}
public async Task<IEnumerable<Product>> GetAllAsync()
{
return await _context.Products
.Include(p => p.Category)
.OrderBy(p => p.Name)
.ToListAsync();
}
public async Task<Product> GetByIdAsync(int id)
{
return await _context.Products
.Include(p => p.Category)
.FirstOrDefaultAsync(p => p.ID == id);
}
public async Task<Product> CreateAsync(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
return product;
}
public async Task<Product> UpdateAsync(Product product)
{
_context.Entry(product).State = EntityState.Modified;
await _context.SaveChangesAsync();
return product;
}
public async Task<bool> DeleteAsync(int id)
{
var product = await GetByIdAsync(id);
if (product == null) return false;
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return true;
}
public async Task<IEnumerable<Category>> GetCategoriesAsync()
{
return await _context.Categories.OrderBy(c => c.Name).ToListAsync();
}
}
@model ProductIndexViewModel
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<h4>产品筛选</h4>
<div class="card">
<div class="card-body">
@using (Html.BeginForm("Index", "Product", FormMethod.Get))
{
<div class="mb-3">
<label>产品名称:</label>
@Html.Kendo().TextBoxFor(m => m.Filter.Name)
.Placeholder("输入产品名称")
.HtmlAttributes(new { @class = "form-control" })
</div>
<div class="mb-3">
<label>分类:</label>
@Html.Kendo().DropDownListFor(m => m.Filter.Category)
.DataTextField("Name")
.DataValueField("Name")
.BindTo(Model.Categories)
.OptionLabel("全部分类")
.HtmlAttributes(new { @class = "form-control" })
</div>
<div class="mb-3">
<label>价格范围:</label>
<div class="row">
<div class="col-6">
@Html.Kendo().NumericTextBoxFor(m => m.Filter.MinPrice)
.Format("c2")
.Placeholder("最低价格")
</div>
<div class="col-6">
@Html.Kendo().NumericTextBoxFor(m => m.Filter.MaxPrice)
.Format("c2")
.Placeholder("最高价格")
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">筛选</button>
<a href="@Url.Action("Index")" class="btn btn-secondary">重置</a>
}
</div>
</div>
</div>
<div class="col-md-9">
<h4>产品管理</h4>
@(Html.Kendo().Grid<Product>()
.Name("productGrid")
.Columns(columns =>
{
columns.Bound(p => p.ID).Width(80);
columns.Bound(p => p.Name).Title("产品名称");
columns.Bound(p => p.Price).Title("价格").Format("{0:C}").Width(120);
columns.Bound(p => p.Category).Title("分类").Width(120);
columns.Bound(p => p.CreatedDate).Title("创建时间")
.Format("{0:yyyy-MM-dd}").Width(120);
columns.Command(command =>
{
command.Edit().Text("编辑");
command.Destroy().Text("删除");
}).Width(160);
})
.ToolBar(toolbar => toolbar.Create().Text("新增产品"))
.Editable(editable => editable.Mode(GridEditMode.PopUp)
.TemplateName("ProductEdit"))
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.Model(model =>
{
model.Id(p => p.ID);
model.Field(p => p.ID).Editable(false);
model.Field(p => p.CreatedDate).Editable(false);
})
.Create(create => create.Action("CreateProduct", "Product"))
.Read(read => read.Action("GetProducts", "Product"))
.Update(update => update.Action("UpdateProduct", "Product"))
.Destroy(destroy => destroy.Action("DeleteProduct", "Product"))
.Events(events => events.Error("onDataSourceError"))
)
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5))
.Sortable()
.Filterable()
.Resizable(resize => resize.Columns(true))
)
</div>
</div>
</div>
<script>
function onDataSourceError(e) {
if (e.errors) {
var message = "数据操作失败:\n";
for (var error in e.errors) {
message += error + "\n";
}
alert(message);
}
}
</script>
本教程深入介绍了Telerik UI的数据绑定与数据源管理:
数据绑定方式:
架构模式支持:
实际应用:通过完整的产品管理系统展示了数据绑定的最佳实践
掌握这些数据绑定技术,能够帮助您构建高效、可维护的数据驱动应用程序。
Progress Telerik Ultimate Collection 2025 Q2下载地址 https://soft51.cc/software/175792580241152290