Various warnings resolved and TreatWarningsAsErrors enabled (#2409)

This commit is contained in:
Vladimir Enchev
2026-01-07 12:29:58 +05:00
committed by GitHub
parent 34fce5188f
commit 0696cd20d5
477 changed files with 6822 additions and 4433 deletions

73
Directory.Build.props Normal file
View File

@@ -0,0 +1,73 @@
<Project>
<!--
Common build properties for all projects in the Radzen.Blazor solution.
To use this file:
1. Rename to Directory.Build.props (remove .sample extension)
2. Adjust settings based on your needs
3. Review the analyzer settings in .editorconfig
This file will be automatically imported by all projects in subdirectories.
-->
<PropertyGroup Label="Language Configuration">
<!-- Use latest C# language features -->
<LangVersion>latest</LangVersion>
<!-- Do NOT enable implicit usings - explicit imports preferred for library code -->
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoWarn>CA2007</NoWarn>
</PropertyGroup>
<PropertyGroup Label="Code Analysis Configuration">
<!-- Enable .NET code analyzers -->
<AnalysisLevel>latest</AnalysisLevel>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<!-- Run analyzers during build and in IDE -->
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
<!-- Don't enforce code style in build (yet) - just show warnings -->
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
<!-- Don't treat warnings as errors (yet) - too many to fix immediately -->
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<!-- Report all analyzer diagnostics -->
<AnalysisMode>All</AnalysisMode>
</PropertyGroup>
<PropertyGroup Label="Build Quality">
<!-- Enable deterministic builds for reproducibility -->
<Deterministic>true</Deterministic>
<!-- Enable deterministic builds in CI/CD -->
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
<!-- Embed source files for better debugging -->
<EmbedAllSources>true</EmbedAllSources>
<DebugType>embedded</DebugType>
</PropertyGroup>
<PropertyGroup Label="Demos and Tests Project Configuration" Condition="$(MSBuildProjectName.Contains('Demos')) OR $(MSBuildProjectName.Contains('Tests'))">
<!-- Demo projects and Tests should not be packable -->
<IsPackable>false</IsPackable>
<!-- DISABLE ALL ANALYZERS FOR DEMO PROJECTS AND TESTS -->
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
</PropertyGroup>
<PropertyGroup Label="Performance">
<!-- Optimize startup time -->
<TieredCompilation>true</TieredCompilation>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>
</PropertyGroup>
</Project>

View File

@@ -22,7 +22,7 @@ namespace Radzen.Blazor.Tests
public Guid Id { get; set; }
public TimeOnly StartTime { get; set; }
public DateOnly BirthDate { get; set; }
public List<int> Scores { get; set; }
public IEnumerable<int> Scores { get; set; }
public List<string> Tags { get; set; }
public List<TestEntity> Children { get; set; }
public Address Address { get; set; }

View File

@@ -605,7 +605,7 @@ namespace Radzen.Blazor.Tests
Assert.True(grid.AllowFieldsPicking);
}
private static IRenderedComponent<RadzenPivotDataGrid<SalesData>> RenderPivotDataGrid(TestContext ctx, Action<ComponentParameterCollectionBuilder<RadzenPivotDataGrid<SalesData>>>? configure = null)
private static IRenderedComponent<RadzenPivotDataGrid<SalesData>> RenderPivotDataGrid(TestContext ctx, Action<ComponentParameterCollectionBuilder<RadzenPivotDataGrid<SalesData>>> configure = null)
{
return ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
{

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>disable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

View File

@@ -6,6 +6,48 @@ root = true
#### Core EditorConfig Options ####
dotnet_diagnostic.CA1002.severity = none
dotnet_diagnostic.CA1003.severity = none
dotnet_diagnostic.CA1024.severity = none
dotnet_diagnostic.CA1030.severity = none
dotnet_diagnostic.CA1031.severity = none
dotnet_diagnostic.CA1033.severity = none
dotnet_diagnostic.CA1044.severity = none
dotnet_diagnostic.CA1050.severity = none
dotnet_diagnostic.CA1051.severity = none
dotnet_diagnostic.CA1052.severity = none
dotnet_diagnostic.CA1054.severity = none
dotnet_diagnostic.CA1055.severity = none
dotnet_diagnostic.CA1056.severity = none
dotnet_diagnostic.CA1063.severity = none
dotnet_diagnostic.CA1068.severity = none
dotnet_diagnostic.CA1308.severity = none
dotnet_diagnostic.CA1708.severity = none
dotnet_diagnostic.CA1711.severity = none
dotnet_diagnostic.CA1716.severity = none
dotnet_diagnostic.CA1720.severity = none
dotnet_diagnostic.CA1721.severity = none
dotnet_diagnostic.CA1724.severity = none
dotnet_diagnostic.CA1725.severity = none
dotnet_diagnostic.CA1802.severity = none
dotnet_diagnostic.CA1814.severity = none
dotnet_diagnostic.CA1815.severity = none
dotnet_diagnostic.CA1816.severity = none
dotnet_diagnostic.CA1819.severity = none
dotnet_diagnostic.CA1822.severity = none
dotnet_diagnostic.CA1827.severity = none
dotnet_diagnostic.CA1834.severity = none
dotnet_diagnostic.CA1845.severity = none
dotnet_diagnostic.CA1849.severity = none
dotnet_diagnostic.CA1851.severity = none
dotnet_diagnostic.CA1859.severity = none
dotnet_diagnostic.CA1863.severity = none
dotnet_diagnostic.CA1869.severity = none
dotnet_diagnostic.CA2007.severity = none
dotnet_diagnostic.CA2012.severity = none
dotnet_diagnostic.CA2211.severity = none
dotnet_diagnostic.CA2227.severity = none
# Indentation and spacing
indent_size = 4
indent_style = space

View File

@@ -21,13 +21,16 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
private readonly Dictionary<string, ConversationSession> sessions = new();
private readonly object sessionsLock = new();
// Add this static field to cache the JsonSerializerOptions instance
private static readonly JsonSerializerOptions CachedJsonSerializerOptions = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
/// <summary>
/// Gets the configuration options for the chat streaming service.
/// </summary>
public AIChatServiceOptions Options => options.Value;
/// <inheritdoc />
public async IAsyncEnumerable<string> GetCompletionsAsync(string userInput, string sessionId = null, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default, string model = null, string systemPrompt = null, double? temperature = null, int? maxTokens = null, string endpoint = null, string proxy = null, string apiKey = null, string apiKeyHeader = null)
public async IAsyncEnumerable<string> GetCompletionsAsync(string userInput, string? sessionId = null, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default, string? model = null, string? systemPrompt = null, double? temperature = null, int? maxTokens = null, string? endpoint = null, string? proxy = null, string? apiKey = null, string? apiKeyHeader = null)
{
if (string.IsNullOrWhiteSpace(userInput))
{
@@ -57,13 +60,18 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
stream = true
};
var request = new HttpRequestMessage(HttpMethod.Post, url)
using var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = new StringContent(JsonSerializer.Serialize(payload, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }), Encoding.UTF8, "application/json")
Content = new StringContent(JsonSerializer.Serialize(payload, CachedJsonSerializerOptions), Encoding.UTF8, "application/json")
};
if (!string.IsNullOrEmpty(effectiveApiKey))
{
if (string.IsNullOrWhiteSpace(effectiveApiKeyHeader))
{
throw new InvalidOperationException("API key header must be specified when an API key is provided.");
}
if (string.Equals(effectiveApiKeyHeader, "Authorization", StringComparison.OrdinalIgnoreCase))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", effectiveApiKey);
@@ -75,22 +83,22 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
}
var httpClient = serviceProvider.GetRequiredService<HttpClient>();
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Chat stream failed: {await response.Content.ReadAsStringAsync(cancellationToken)}");
throw new HttpRequestException($"Chat stream failed: {await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false)}");
}
using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
using var reader = new StreamReader(stream);
var assistantResponse = new StringBuilder();
string line;
string? line;
while ((line = await reader.ReadLineAsync()) is not null && !cancellationToken.IsCancellationRequested)
{
if (string.IsNullOrWhiteSpace(line) || !line.StartsWith("data:"))
if (string.IsNullOrWhiteSpace(line) || !line.StartsWith("data:", StringComparison.Ordinal))
{
continue;
}
@@ -118,7 +126,7 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
}
/// <inheritdoc />
public ConversationSession GetOrCreateSession(string sessionId = null)
public ConversationSession GetOrCreateSession(string? sessionId = null)
{
lock (sessionsLock)
{
@@ -182,9 +190,14 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
private static string ParseStreamingResponse(string json)
{
if (string.IsNullOrWhiteSpace(json))
{
return string.Empty;
}
try
{
var doc = JsonDocument.Parse(json);
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (!root.TryGetProperty("choices", out var choices) || choices.GetArrayLength() == 0)
@@ -206,7 +219,11 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
return string.Empty;
}
catch
catch (JsonException)
{
return string.Empty;
}
catch (FormatException)
{
return string.Empty;
}

View File

@@ -16,15 +16,8 @@ public static class AIChatServiceExtensions
/// <returns>The updated service collection.</returns>
public static IServiceCollection AddAIChatService(this IServiceCollection services, Action<AIChatServiceOptions> configureOptions)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
}
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configureOptions);
services.Configure(configureOptions);
services.AddScoped<IAIChatService, AIChatService>();

View File

@@ -13,7 +13,7 @@ public class AIChatServiceOptions
/// <summary>
/// Gets or sets the proxy URL for the AI service, if any. If set, this will override the Endpoint.
/// </summary>
public string Proxy { get; set; } = null;
public string? Proxy { get; set; }
/// <summary>
/// Gets or sets the API key for authentication with the AI service.
@@ -28,7 +28,7 @@ public class AIChatServiceOptions
/// <summary>
/// Gets or sets the model name to use for executing chat completions (e.g., 'gpt-3.5-turbo').
/// </summary>
public string Model { get; set; }
public string? Model { get; set; }
/// <summary>
/// Gets or sets the system prompt for the AI assistant.

View File

@@ -22,19 +22,19 @@ namespace Radzen.Blazor
/// Gets or sets the text of the appointment.
/// </summary>
/// <value>The text.</value>
public string Text { get; set; }
public string? Text { get; set; }
/// <summary>
/// Gets or sets the data associated with the appointment
/// </summary>
/// <value>The data.</value>
public object Data { get; set; }
public object? Data { get; set; }
/// <summary>
/// Determines whether the specified object is equal to this instance. Used to check if two appointments are equal.
/// </summary>
/// <param name="obj">The object to compare with this instance.</param>
/// <returns><c>true</c> if the specified is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is AppointmentData data &&
Start == data.Start &&

View File

@@ -13,7 +13,7 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The stroke.</value>
[Parameter]
public string Stroke { get; set; }
public string? Stroke { get; set; }
/// <summary>
/// Gets or sets the pixel width of axis.
/// </summary>
@@ -26,21 +26,21 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The child content.</value>
[Parameter]
public RenderFragment ChildContent { get; set; }
public RenderFragment? ChildContent { get; set; }
/// <summary>
/// Gets or sets the format string used to display the axis values.
/// </summary>
/// <value>The format string.</value>
[Parameter]
public string FormatString { get; set; }
public string? FormatString { get; set; }
/// <summary>
/// Gets or sets a formatter function that formats the axis values.
/// </summary>
/// <value>The formatter.</value>
[Parameter]
public Func<object, string> Formatter { get; set; }
public Func<object, string>? Formatter { get; set; }
/// <summary>
/// Gets or sets the type of the line used to display the axis.
@@ -80,20 +80,20 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The minimum.</value>
[Parameter]
public object Min { get; set; }
public object? Min { get; set; }
/// <summary>
/// Specifies the maximum value of the axis.
/// </summary>
/// <value>The maximum.</value>
[Parameter]
public object Max { get; set; }
public object? Max { get; set; }
/// <summary>
/// Specifies the step of the axis.
/// </summary>
[Parameter]
public object Step { get; set; }
public object? Step { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="AxisBase"/> is visible.
@@ -139,7 +139,7 @@ namespace Radzen.Blazor
}
else
{
return scale.FormatTick(FormatString, value);
return scale.FormatTick(FormatString ?? string.Empty, value);
}
}

View File

@@ -19,7 +19,17 @@ namespace Radzen.Blazor
/// Cache for the value returned by <see cref="Category"/> when that value is only dependent on
/// <see cref="CategoryProperty"/>.
/// </summary>
Func<TItem, double> categoryPropertyCache;
Func<TItem, double>? categoryPropertyCache;
/// <summary>
/// Returns the parent <see cref="RadzenChart"/> instance or throws an <see cref="InvalidOperationException"/> if not present.
/// </summary>
/// <returns>The parent <see cref="RadzenChart"/>.</returns>
/// <exception cref="InvalidOperationException">Thrown when the parent chart is not set.</exception>
protected RadzenChart RequireChart()
{
return Chart ?? throw new InvalidOperationException($"{GetType().Name} requires a parent RadzenChart.");
}
/// <summary>
/// Creates a getter function that returns a value from the specified category scale for the specified data item.
@@ -34,13 +44,13 @@ namespace Radzen.Blazor
if (IsNumeric(CategoryProperty))
{
categoryPropertyCache = PropertyAccess.Getter<TItem, double>(CategoryProperty);
categoryPropertyCache = PropertyAccess.Getter<TItem, double>(CategoryProperty!);
return categoryPropertyCache;
}
if (IsDate(CategoryProperty))
{
var category = PropertyAccess.Getter<TItem, DateTime>(CategoryProperty);
var category = PropertyAccess.Getter<TItem, DateTime>(CategoryProperty!);
categoryPropertyCache = (item) => category(item).Ticks;
return categoryPropertyCache;
}
@@ -49,7 +59,7 @@ namespace Radzen.Blazor
{
Func<TItem, object> category = String.IsNullOrEmpty(CategoryProperty) ? (item) => string.Empty : PropertyAccess.Getter<TItem, object>(CategoryProperty);
return (item) => ordinal.Data.IndexOf(category(item));
return (item) => ordinal.Data?.IndexOf(category(item)) ?? -1;
}
return (item) => Items.IndexOf(item);
@@ -60,6 +70,8 @@ namespace Radzen.Blazor
/// </summary>
protected Func<TItem, double> ComposeCategory(ScaleBase scale)
{
ArgumentNullException.ThrowIfNull(scale);
return scale.Compose(Category(scale));
}
@@ -68,6 +80,8 @@ namespace Radzen.Blazor
/// </summary>
protected Func<TItem, double> ComposeValue(ScaleBase scale)
{
ArgumentNullException.ThrowIfNull(scale);
return scale.Compose(Value);
}
@@ -77,7 +91,7 @@ namespace Radzen.Blazor
/// <param name="propertyName">Name of the property.</param>
/// <returns><c>true</c> if the specified property name is date; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentException">Property {propertyName} does not exist</exception>
protected bool IsDate(string propertyName)
protected bool IsDate(string? propertyName)
{
if (String.IsNullOrEmpty(propertyName))
{
@@ -106,7 +120,7 @@ namespace Radzen.Blazor
/// <param name="propertyName">Name of the property.</param>
/// <returns><c>true</c> if the specified property name is numeric; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentException">Property {propertyName} does not exist</exception>
protected bool IsNumeric(string propertyName)
protected bool IsNumeric(string? propertyName)
{
if (String.IsNullOrEmpty(propertyName))
{
@@ -125,21 +139,21 @@ namespace Radzen.Blazor
/// <inheritdoc />
[Parameter]
public string Title { get; set; }
public string Title { get; set; } = null!;
/// <summary>
/// Gets or sets the child content.
/// </summary>
/// <value>The child content.</value>
[Parameter]
public RenderFragment ChildContent { get; set; }
public RenderFragment? ChildContent { get; set; }
/// <summary>
/// Gets or sets the tooltip template.
/// </summary>
/// <value>The tooltip template.</value>
[Parameter]
public RenderFragment<TItem> TooltipTemplate { get; set; }
public RenderFragment<TItem>? TooltipTemplate { get; set; }
/// <summary>
/// Gets the list of overlays.
@@ -157,7 +171,7 @@ namespace Radzen.Blazor
/// The name of the property of <typeparamref name="TItem" /> that provides the X axis (a.k.a. category axis) values.
/// </summary>
[Parameter]
public string CategoryProperty { get; set; }
public string? CategoryProperty { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="CartesianSeries{TItem}"/> is visible.
@@ -194,7 +208,7 @@ namespace Radzen.Blazor
/// The name of the property of <typeparamref name="TItem" /> that provides the Y axis (a.k.a. value axis) values.
/// </summary>
[Parameter]
public string ValueProperty { get; set; }
public string? ValueProperty { get; set; }
/// <inheritdoc />
[Parameter]
@@ -223,7 +237,7 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The data.</value>
[Parameter]
public IEnumerable<TItem> Data { get; set; }
public IEnumerable<TItem>? Data { get; set; }
/// <summary>
/// Stores <see cref="Data" /> as an IList of <typeparamref name="TItem"/>.
@@ -272,6 +286,8 @@ namespace Radzen.Blazor
/// <inheritdoc />
public virtual ScaleBase TransformCategoryScale(ScaleBase scale)
{
ArgumentNullException.ThrowIfNull(scale);
if (Items == null)
{
return scale;
@@ -298,7 +314,7 @@ namespace Radzen.Blazor
var data = GetCategories();
if (scale is OrdinalScale ordinal)
if (scale is OrdinalScale ordinal && ordinal.Data != null)
{
foreach (var item in ordinal.Data)
{
@@ -328,6 +344,8 @@ namespace Radzen.Blazor
/// <inheritdoc />
public virtual ScaleBase TransformValueScale(ScaleBase scale)
{
ArgumentNullException.ThrowIfNull(scale);
if (Items != null)
{
if (Items.Any())
@@ -398,29 +416,32 @@ namespace Radzen.Blazor
{
if (Data != null)
{
if (Data is IList<TItem>)
if (Data is IList<TItem> list)
{
Items = Data as IList<TItem>;
Items = list;
}
else
{
Items = Data.ToList();
}
if (IsDate(CategoryProperty) || IsNumeric(CategoryProperty))
if (!string.IsNullOrEmpty(CategoryProperty) && (IsDate(CategoryProperty) || IsNumeric(CategoryProperty)))
{
Items = Items.AsQueryable().OrderBy(CategoryProperty).ToList();
}
}
await Chart.Refresh(false);
if (Chart != null)
{
await Chart.Refresh(false);
}
}
}
/// <inheritdoc />
protected override void Initialize()
{
Chart.AddSeries(this);
Chart?.AddSeries(this);
}
/// <inheritdoc />
@@ -443,6 +464,9 @@ namespace Radzen.Blazor
/// <returns><c>true</c> if the polygon contains the point, <c>false</c> otherwise.</returns>
protected bool InsidePolygon(Point point, Point[] polygon)
{
ArgumentNullException.ThrowIfNull(point);
ArgumentNullException.ThrowIfNull(polygon);
var minX = polygon[0].X;
var maxX = polygon[0].X;
var minY = polygon[0].Y;
@@ -479,18 +503,22 @@ namespace Radzen.Blazor
/// <inheritdoc />
public virtual RenderFragment RenderTooltip(object data)
{
var chart = RequireChart();
var item = (TItem)data;
return builder =>
{
if (Chart.Tooltip.Shared)
if (chart.Tooltip.Shared)
{
var category = PropertyAccess.GetValue(item, CategoryProperty);
builder.OpenComponent<ChartSharedTooltip>(0);
builder.AddAttribute(1, nameof(ChartSharedTooltip.Class), TooltipClass(item));
builder.AddAttribute(2, nameof(ChartSharedTooltip.Title), TooltipTitle(item));
builder.AddAttribute(3, nameof(ChartSharedTooltip.ChildContent), RenderSharedTooltipItems(category));
builder.CloseComponent();
var category = !string.IsNullOrEmpty(CategoryProperty) ? PropertyAccess.GetValue(item, CategoryProperty) : null;
if (category != null)
{
builder.OpenComponent<ChartSharedTooltip>(0);
builder.AddAttribute(1, nameof(ChartSharedTooltip.Class), TooltipClass(item));
builder.AddAttribute(2, nameof(ChartSharedTooltip.Title), TooltipTitle(item));
builder.AddAttribute(3, nameof(ChartSharedTooltip.ChildContent), RenderSharedTooltipItems(category));
builder.CloseComponent();
}
}
else
{
@@ -508,9 +536,11 @@ namespace Radzen.Blazor
private RenderFragment RenderSharedTooltipItems(object category)
{
var chart = RequireChart();
return builder =>
{
var visibleSeries = Chart.Series.Where(s => s.Visible).ToList();
var visibleSeries = chart.Series.Where(s => s.Visible).ToList();
foreach (var series in visibleSeries)
{
@@ -524,7 +554,7 @@ namespace Radzen.Blazor
{
return builder =>
{
var item = Items.FirstOrDefault(i => object.Equals(PropertyAccess.GetValue(i, CategoryProperty), category));
var item = Items.FirstOrDefault(i => !string.IsNullOrEmpty(CategoryProperty) && object.Equals(PropertyAccess.GetValue(i, CategoryProperty), category));
if (item != null)
{
@@ -553,7 +583,7 @@ namespace Radzen.Blazor
/// <param name="item">The item.</param>
protected virtual string TooltipStyle(TItem item)
{
return Chart.Tooltip.Style;
return Chart?.Tooltip?.Style ?? string.Empty;
}
/// <summary>
@@ -562,7 +592,13 @@ namespace Radzen.Blazor
/// <param name="item">The item.</param>
protected virtual string TooltipClass(TItem item)
{
return $"rz-series-{Chart.Series.IndexOf(this)}-tooltip";
var chart = Chart;
if (chart == null)
{
return "rz-series-tooltip";
}
return $"rz-series-{chart.Series.IndexOf(this)}-tooltip";
}
/// <inheritdoc />
@@ -576,6 +612,8 @@ namespace Radzen.Blazor
/// </summary>
protected virtual RenderFragment RenderLegendItem(bool clickable)
{
var chart = RequireChart();
var index = chart.Series.IndexOf(this);
var style = new List<string>();
if (IsVisible == false)
@@ -586,7 +624,7 @@ namespace Radzen.Blazor
return builder =>
{
builder.OpenComponent<LegendItem>(0);
builder.AddAttribute(1, nameof(LegendItem.Index), Chart.Series.IndexOf(this));
builder.AddAttribute(1, nameof(LegendItem.Index), index);
builder.AddAttribute(2, nameof(LegendItem.Color), Color);
builder.AddAttribute(3, nameof(LegendItem.MarkerType), MarkerType);
builder.AddAttribute(4, nameof(LegendItem.Style), string.Join(";", style));
@@ -617,19 +655,35 @@ namespace Radzen.Blazor
/// <inheritdoc />
public double GetMedian()
{
return Data.Select(e => Value(e)).OrderBy(e => e).Skip(Data.Count() / 2).FirstOrDefault();
var values = Items.Select(Value).OrderBy(e => e).ToList();
if (values.Count == 0)
{
return 0;
}
return values[values.Count / 2];
}
/// <inheritdoc />
public double GetMean()
{
return Data.Select(e => Value(e)).DefaultIfEmpty(double.NaN).Average();
return Items.Any() ? Items.Select(Value).Average() : double.NaN;
}
/// <inheritdoc />
public double GetMode()
{
return Data.Any() ? Data.GroupBy(e => Value(e)).Select(g => new { Value = g.Key, Count = g.Count() }).OrderByDescending(e => e.Count).FirstOrDefault().Value : double.NaN;
if (!Items.Any())
{
return double.NaN;
}
return Items
.GroupBy(item => Value(item))
.Select(g => new { Value = g.Key, Count = g.Count() })
.OrderByDescending(e => e.Count)
.First()
.Value;
}
/// <summary>
@@ -639,33 +693,43 @@ namespace Radzen.Blazor
{
double a = double.NaN, b = double.NaN;
if (Data.Any())
var chart = Chart;
if (chart == null)
{
return (a, b);
}
if (Items.Any())
{
Func<TItem, double> X;
Func<TItem, double> Y;
if (Chart.ShouldInvertAxes())
if (chart.ShouldInvertAxes())
{
X = e => Chart.CategoryScale.Scale(Value(e));
Y = e => Chart.ValueScale.Scale(Category(Chart.ValueScale)(e));
var valueScale = chart.ValueScale;
var categoryAccessor = Category(chart.ValueScale);
X = e => chart.CategoryScale.Scale(Value(e));
Y = e => valueScale.Scale(categoryAccessor(e));
}
else
{
X = e => Chart.CategoryScale.Scale(Category(Chart.CategoryScale)(e));
Y = e => Chart.ValueScale.Scale(Value(e));
var categoryAccessor = Category(chart.CategoryScale);
X = e => chart.CategoryScale.Scale(categoryAccessor(e));
Y = e => chart.ValueScale.Scale(Value(e));
}
var avgX = Data.Select(e => X(e)).Average();
var avgY = Data.Select(e => Y(e)).Average();
var sumXY = Data.Sum(e => (X(e) - avgX) * (Y(e) - avgY));
if (Chart.ShouldInvertAxes())
var data = Items.ToList();
var avgX = data.Select(e => X(e)).Average();
var avgY = data.Select(e => Y(e)).Average();
var sumXY = data.Sum(e => (X(e) - avgX) * (Y(e) - avgY));
if (chart.ShouldInvertAxes())
{
var sumYSq = Data.Sum(e => (Y(e) - avgY) * (Y(e) - avgY));
var sumYSq = data.Sum(e => (Y(e) - avgY) * (Y(e) - avgY));
b = sumXY / sumYSq;
a = avgX - b * avgY;
}
else
{
var sumXSq = Data.Sum(e => (X(e) - avgX) * (X(e) - avgX));
var sumXSq = data.Sum(e => (X(e) - avgX) * (X(e) - avgX));
b = sumXY / sumXSq;
a = avgY - b * avgX;
}
@@ -678,7 +742,9 @@ namespace Radzen.Blazor
{
IsVisible = !IsVisible;
if (Chart.LegendClick.HasDelegate)
var chart = Chart;
if (chart?.LegendClick.HasDelegate == true)
{
var args = new LegendClickEventArgs
{
@@ -687,18 +753,28 @@ namespace Radzen.Blazor
IsVisible = IsVisible,
};
await Chart.LegendClick.InvokeAsync(args);
await chart.LegendClick.InvokeAsync(args);
IsVisible = args.IsVisible;
}
await Chart.Refresh();
if (chart != null)
{
await chart.Refresh();
}
}
/// <inheritdoc />
public string GetTitle()
{
return String.IsNullOrEmpty(Title) ? $"Series {Chart.Series.IndexOf(this) + 1}" : Title;
var chart = Chart;
if (string.IsNullOrEmpty(Title))
{
var index = chart?.Series.IndexOf(this) ?? 0;
return $"Series {index + 1}";
}
return Title;
}
/// <summary>
@@ -716,8 +792,9 @@ namespace Radzen.Blazor
/// <param name="item">The item.</param>
protected virtual string TooltipTitle(TItem item)
{
var category = Category(Chart.CategoryScale);
return Chart.CategoryAxis.Format(Chart.CategoryScale, Chart.CategoryScale.Value(category(item)));
var chart = RequireChart();
var category = Category(chart.CategoryScale);
return chart.CategoryAxis.Format(chart.CategoryScale, chart.CategoryScale.Value(category(item)));
}
/// <summary>
@@ -727,7 +804,8 @@ namespace Radzen.Blazor
/// <returns>System.String.</returns>
protected virtual string TooltipValue(TItem item)
{
return Chart.ValueAxis.Format(Chart.ValueScale, Chart.ValueScale.Value(Value(item)));
var chart = RequireChart();
return chart.ValueAxis.Format(chart.ValueScale, chart.ValueScale.Value(Value(item)));
}
/// <summary>
@@ -736,8 +814,9 @@ namespace Radzen.Blazor
/// <param name="item">The item.</param>
internal virtual double TooltipX(TItem item)
{
var category = Category(Chart.CategoryScale);
return Chart.CategoryScale.Scale(category(item), true);
var chart = RequireChart();
var category = Category(chart.CategoryScale);
return chart.CategoryScale.Scale(category(item), true);
}
/// <summary>
@@ -746,7 +825,8 @@ namespace Radzen.Blazor
/// <param name="item">The item.</param>
internal virtual double TooltipY(TItem item)
{
return Chart.ValueScale.Scale(Value(item), true);
var chart = RequireChart();
return chart.ValueScale.Scale(Value(item), true);
}
/// <inheritdoc />
@@ -760,25 +840,26 @@ namespace Radzen.Blazor
return new { Item = item, Distance = distance };
}).Aggregate((a, b) => a.Distance < b.Distance ? a : b).Item;
return (retObject,
return (retObject!,
new Point() { X = TooltipX(retObject), Y = TooltipY(retObject)});
}
return (null, null);
return (default!, new Point());
}
/// <inheritdoc />
public virtual IEnumerable<ChartDataLabel> GetDataLabels(double offsetX, double offsetY)
{
var chart = RequireChart();
var list = new List<ChartDataLabel>();
foreach (var d in Data)
foreach (var d in Items)
{
list.Add(new ChartDataLabel
{
Position = new Point { X = TooltipX(d) + offsetX, Y = TooltipY(d) + offsetY },
TextAnchor = "middle",
Text = Chart.ValueAxis.Format(Chart.ValueScale, Value(d))
Text = chart.ValueAxis.Format(chart.ValueScale, Value(d))
});
}
@@ -793,12 +874,12 @@ namespace Radzen.Blazor
/// <param name="defaultValue">The default value.</param>
/// <param name="colorRange">The color range value.</param>
/// <param name="value">The value of the item.</param>
protected string PickColor(int index, IEnumerable<string> colors, string defaultValue = null, IList<SeriesColorRange> colorRange = null, double value = 0.0)
protected string? PickColor(int index, IEnumerable<string>? colors, string? defaultValue = null, IList<SeriesColorRange>? colorRange = null, double value = 0.0)
{
if (colorRange != null)
{
var result = colorRange.Where(r => r.Min <= value && r.Max >= value).FirstOrDefault<SeriesColorRange>();
return result != null ? result.Color : defaultValue;
return result?.Color ?? defaultValue;
}
else
{
@@ -819,18 +900,20 @@ namespace Radzen.Blazor
/// <inheritdoc />
public async Task InvokeClick(EventCallback<SeriesClickEventArgs> handler, object data)
{
var category = Category(Chart.CategoryScale);
var chart = RequireChart();
var category = Category(chart.CategoryScale);
var dataItem = (TItem)data;
await handler.InvokeAsync(new SeriesClickEventArgs
{
Data = data,
Title = GetTitle(),
Category = PropertyAccess.GetValue(data, CategoryProperty),
Value = PropertyAccess.GetValue(data, ValueProperty),
Category = !string.IsNullOrEmpty(CategoryProperty) ? PropertyAccess.GetValue(data, CategoryProperty) : null,
Value = !string.IsNullOrEmpty(ValueProperty) ? PropertyAccess.GetValue(data, ValueProperty) : null,
Point = new SeriesPoint
{
Category = category((TItem)data),
Value = Value((TItem)data)
Category = category(dataItem),
Value = Value(dataItem)
}
});
}

View File

@@ -39,5 +39,5 @@ public class ChatMessage
/// <summary>
/// Gets or sets the role associated with the message (e.g., "user", "assistant").
/// </summary>
public string Role { get; set; }
public string? Role { get; set; }
}

View File

@@ -12,25 +12,25 @@ public class CompositeFilterDescriptor
/// Gets or sets the name of the filtered property.
/// </summary>
/// <value>The property.</value>
public string Property { get; set; }
public string? Property { get; set; }
/// <summary>
/// Gets or sets the property type.
/// </summary>
/// <value>The property type.</value>
public Type Type { get; set; }
public Type? Type { get; set; }
/// <summary>
/// Gets or sets the name of the filtered property.
/// </summary>
/// <value>The property.</value>
public string FilterProperty { get; set; }
public string? FilterProperty { get; set; }
/// <summary>
/// Gets or sets the value to filter by.
/// </summary>
/// <value>The filter value.</value>
public object FilterValue { get; set; }
public object? FilterValue { get; set; }
/// <summary>
/// Gets or sets the operator which will compare the property value with <see cref="FilterValue" />.
@@ -48,6 +48,6 @@ public class CompositeFilterDescriptor
/// Gets or sets the filters.
/// </summary>
/// <value>The filters.</value>
public IEnumerable<CompositeFilterDescriptor> Filters { get; set; }
public IEnumerable<CompositeFilterDescriptor>? Filters { get; set; }
}

View File

@@ -1,225 +1,227 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Radzen
{
/// <summary>
/// Class ContextMenuService. Contains various methods with options to open and close context menus.
/// Should be added as scoped service in the application services and RadzenContextMenu should be added in application main layout.
/// Implements the <see cref="IDisposable" />
/// </summary>
/// <seealso cref="IDisposable" />
/// <example>
/// <code>
/// @inject ContextMenuService ContextMenuService
/// &lt;RadzenButton Text="Show context menu" ContextMenu=@(args => ShowContextMenuWithContent(args)) /&gt;
/// @code {
/// void ShowContextMenuWithContent(MouseEventArgs args) =&gt; ContextMenuService.Open(args, ds =&gt;
/// @&lt;RadzenMenu Click="OnMenuItemClick"&gt;
/// &lt;RadzenMenuItem Text="Item1" Value="1"&gt;&lt;/RadzenMenuItem&gt;
/// &lt;RadzenMenuItem Text="Item2" Value="2"&gt;&lt;/RadzenMenuItem&gt;
/// &lt;RadzenMenuItem Text="More items" Value="3"&gt;
/// &lt;RadzenMenuItem Text="More sub items" Value="4"&gt;
/// &lt;RadzenMenuItem Text="Item1" Value="5"&gt;&lt;/RadzenMenuItem&gt;
/// &lt;RadzenMenuItem Text="Item2" Value="6"&gt;&lt;/RadzenMenuItem&gt;
/// &lt;/RadzenMenuItem&gt;
/// &lt;/RadzenMenuItem&gt;
/// &lt;/RadzenMenu&gt;);
///
/// void OnMenuItemClick(MenuItemEventArgs args)
/// {
/// Console.WriteLine($"Menu item with Value={args.Value} clicked");
/// }
/// }
/// </code>
/// </example>
public class ContextMenuService : IDisposable
{
/// <summary>
/// Gets or sets the navigation manager.
/// </summary>
/// <value>The navigation manager.</value>
NavigationManager navigationManager { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ContextMenuService"/> class.
/// </summary>
/// <param name="uriHelper">The URI helper.</param>
public ContextMenuService(NavigationManager uriHelper)
{
navigationManager = uriHelper;
if (navigationManager != null)
{
navigationManager.LocationChanged += UriHelper_OnLocationChanged;
}
}
/// <summary>
/// Handles the OnLocationChanged event of the UriHelper control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs"/> instance containing the event data.</param>
private void UriHelper_OnLocationChanged(object sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System;
using System.Collections.Generic;
namespace Radzen
{
/// <summary>
/// Class ContextMenuService. Contains various methods with options to open and close context menus.
/// Should be added as scoped service in the application services and RadzenContextMenu should be added in application main layout.
/// Implements the <see cref="IDisposable" />
/// </summary>
/// <seealso cref="IDisposable" />
/// <example>
/// <code>
/// @inject ContextMenuService ContextMenuService
/// &lt;RadzenButton Text="Show context menu" ContextMenu=@(args => ShowContextMenuWithContent(args)) /&gt;
/// @code {
/// void ShowContextMenuWithContent(MouseEventArgs args) =&gt; ContextMenuService.Open(args, ds =&gt;
/// @&lt;RadzenMenu Click="OnMenuItemClick"&gt;
/// &lt;RadzenMenuItem Text="Item1" Value="1"&gt;&lt;/RadzenMenuItem&gt;
/// &lt;RadzenMenuItem Text="Item2" Value="2"&gt;&lt;/RadzenMenuItem&gt;
/// &lt;RadzenMenuItem Text="More items" Value="3"&gt;
/// &lt;RadzenMenuItem Text="More sub items" Value="4"&gt;
/// &lt;RadzenMenuItem Text="Item1" Value="5"&gt;&lt;/RadzenMenuItem&gt;
/// &lt;RadzenMenuItem Text="Item2" Value="6"&gt;&lt;/RadzenMenuItem&gt;
/// &lt;/RadzenMenuItem&gt;
/// &lt;/RadzenMenuItem&gt;
/// &lt;/RadzenMenu&gt;);
///
/// void OnMenuItemClick(MenuItemEventArgs args)
/// {
/// Console.WriteLine($"Menu item with Value={args.Value} clicked");
/// }
/// }
/// </code>
/// </example>
public class ContextMenuService : IDisposable
{
/// <summary>
/// Gets or sets the navigation manager.
/// </summary>
/// <value>The navigation manager.</value>
NavigationManager? navigationManager { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ContextMenuService"/> class.
/// </summary>
/// <param name="uriHelper">The URI helper.</param>
public ContextMenuService(NavigationManager? uriHelper)
{
this.OnNavigate?.Invoke();
}
/// <summary>
/// Occurs when [on navigate].
/// </summary>
public event Action OnNavigate;
/// <summary>
/// Raises the Close event.
/// </summary>
public event Action OnClose;
/// <summary>
/// Occurs when [on open].
/// </summary>
public event Action<MouseEventArgs, ContextMenuOptions> OnOpen;
/// <summary>
/// Opens the specified arguments.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <param name="items">The items.</param>
/// <param name="click">The click.</param>
public void Open(MouseEventArgs args, IEnumerable<ContextMenuItem> items, Action<MenuItemEventArgs> click = null)
{
var options = new ContextMenuOptions();
options.Items = items;
options.Click = click;
OpenTooltip(args, options);
}
/// <summary>
/// Opens the specified arguments.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <param name="childContent">Content of the child.</param>
public void Open(MouseEventArgs args, RenderFragment<ContextMenuService> childContent)
{
var options = new ContextMenuOptions();
options.ChildContent = childContent;
OpenTooltip(args, options);
}
/// <summary>
/// Opens the tooltip.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <param name="options">The options.</param>
private void OpenTooltip(MouseEventArgs args, ContextMenuOptions options)
{
OnOpen?.Invoke(args, options);
}
/// <summary>
/// Closes this instance.
/// </summary>
public void Close()
{
OnClose?.Invoke();
}
/// <summary>
/// Disposes this instance.
/// </summary>
public void Dispose()
{
navigationManager.LocationChanged -= UriHelper_OnLocationChanged;
}
}
/// <summary>
/// Class ContextMenuOptions.
/// </summary>
public class ContextMenuOptions
{
/// <summary>
/// Gets or sets the child content.
/// </summary>
/// <value>The child content.</value>
public RenderFragment<ContextMenuService> ChildContent { get; set; }
/// <summary>
/// Gets or sets the items.
/// </summary>
/// <value>The items.</value>
public IEnumerable<ContextMenuItem> Items { get; set; }
/// <summary>
/// Gets or sets the click.
/// </summary>
/// <value>The click.</value>
public Action<MenuItemEventArgs> Click { get; set; }
}
/// <summary>
/// Class ContextMenu.
/// </summary>
public class ContextMenu
{
/// <summary>
/// Gets or sets the options.
/// </summary>
/// <value>The options.</value>
public ContextMenuOptions Options { get; set; }
/// <summary>
/// Gets or sets the mouse event arguments.
/// </summary>
/// <value>The mouse event arguments.</value>
public MouseEventArgs MouseEventArgs { get; set; }
}
/// <summary>
/// Class ContextMenuItem.
/// </summary>
public class ContextMenuItem
{
/// <summary>
/// Gets or sets the text.
/// </summary>
/// <value>The text.</value>
public string Text { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public object Value { get; set; }
/// <summary>
/// Gets or sets the icon.
/// </summary>
/// <value>The icon.</value>
public string Icon { get; set; }
/// <summary>
/// Gets or sets the icon color.
/// </summary>
/// <value>The icon color.</value>
public string IconColor { get; set; }
/// <summary>
/// Gets or sets the image.
/// </summary>
/// <value>The image.</value>
public string Image { get; set; }
/// <summary>
/// Gets or sets the image style.
/// </summary>
/// <value>The image style.</value>
public string ImageStyle { get; set; }
/// <summary>
/// Gets a value indicating whether this instance is disabled.
/// </summary>
/// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value>
public bool Disabled { get; set; }
}
}
navigationManager = uriHelper;
if (navigationManager != null)
{
navigationManager.LocationChanged += UriHelper_OnLocationChanged;
}
}
/// <summary>
/// Handles the OnLocationChanged event of the UriHelper control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs"/> instance containing the event data.</param>
private void UriHelper_OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
{
this.OnNavigate?.Invoke();
}
/// <summary>
/// Occurs when [on navigate].
/// </summary>
public event Action? OnNavigate;
/// <summary>
/// Raises the Close event.
/// </summary>
public event Action? OnClose;
/// <summary>
/// Occurs when [on open].
/// </summary>
public event Action<MouseEventArgs, ContextMenuOptions>? OnOpen;
/// <summary>
/// Opens the specified arguments.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <param name="items">The items.</param>
/// <param name="click">The click.</param>
public void Open(MouseEventArgs args, IEnumerable<ContextMenuItem> items, Action<MenuItemEventArgs>? click = null)
{
var options = new ContextMenuOptions();
options.Items = items;
options.Click = click;
OpenTooltip(args, options);
}
/// <summary>
/// Opens the specified arguments.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <param name="childContent">Content of the child.</param>
public void Open(MouseEventArgs args, RenderFragment<ContextMenuService> childContent)
{
var options = new ContextMenuOptions();
options.ChildContent = childContent;
OpenTooltip(args, options);
}
/// <summary>
/// Opens the tooltip.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <param name="options">The options.</param>
private void OpenTooltip(MouseEventArgs args, ContextMenuOptions options)
{
OnOpen?.Invoke(args, options);
}
/// <summary>
/// Closes this instance.
/// </summary>
public void Close()
{
OnClose?.Invoke();
}
/// <summary>
/// Disposes this instance.
/// </summary>
public void Dispose()
{
if (navigationManager != null)
{
navigationManager.LocationChanged -= UriHelper_OnLocationChanged;
}
GC.SuppressFinalize(this);
}
}
/// <summary>
/// Class ContextMenuOptions.
/// </summary>
public class ContextMenuOptions
{
/// <summary>
/// Gets or sets the child content.
/// </summary>
/// <value>The child content.</value>
public RenderFragment<ContextMenuService>? ChildContent { get; set; }
/// <summary>
/// Gets or sets the items.
/// </summary>
/// <value>The items.</value>
public IEnumerable<ContextMenuItem>? Items { get; set; }
/// <summary>
/// Gets or sets the click.
/// </summary>
/// <value>The click.</value>
public Action<MenuItemEventArgs>? Click { get; set; }
}
/// <summary>
/// Class ContextMenu.
/// </summary>
public class ContextMenu
{
/// <summary>
/// Gets or sets the options.
/// </summary>
/// <value>The options.</value>
public ContextMenuOptions? Options { get; set; }
/// <summary>
/// Gets or sets the mouse event arguments.
/// </summary>
/// <value>The mouse event arguments.</value>
public MouseEventArgs? MouseEventArgs { get; set; }
}
/// <summary>
/// Class ContextMenuItem.
/// </summary>
public class ContextMenuItem
{
/// <summary>
/// Gets or sets the text.
/// </summary>
/// <value>The text.</value>
public string? Text { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public object? Value { get; set; }
/// <summary>
/// Gets or sets the icon.
/// </summary>
/// <value>The icon.</value>
public string? Icon { get; set; }
/// <summary>
/// Gets or sets the icon color.
/// </summary>
/// <value>The icon color.</value>
public string? IconColor { get; set; }
/// <summary>
/// Gets or sets the image.
/// </summary>
/// <value>The image.</value>
public string? Image { get; set; }
/// <summary>
/// Gets or sets the image style.
/// </summary>
/// <value>The image style.</value>
public string? ImageStyle { get; set; }
/// <summary>
/// Gets a value indicating whether this instance is disabled.
/// </summary>
/// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value>
public bool Disabled { get; set; }
}
}

View File

@@ -18,25 +18,39 @@ public static class ConvertType
/// <param name="type">The type.</param>
/// <param name="culture">The culture.</param>
/// <returns>System.Object</returns>
public static object ChangeType(object value, Type type, CultureInfo culture = null)
public static object? ChangeType(object value, Type type, CultureInfo? culture = null)
{
ArgumentNullException.ThrowIfNull(type);
// CA1062: Validate 'value' is non-null before using it
if (value == null)
{
if (Nullable.GetUnderlyingType(type) != null)
{
return null;
}
throw new ArgumentNullException(nameof(value));
}
if (culture == null)
{
culture = CultureInfo.CurrentCulture;
}
if (value == null && Nullable.GetUnderlyingType(type) != null)
{
return value;
}
if ((Nullable.GetUnderlyingType(type) ?? type) == typeof(Guid) && value is string)
{
return Guid.Parse((string)value);
}
if (Nullable.GetUnderlyingType(type)?.IsEnum == true)
var underlyingEnumType = Nullable.GetUnderlyingType(type);
if (underlyingEnumType?.IsEnum == true)
{
return Enum.Parse(Nullable.GetUnderlyingType(type), value.ToString());
var valueString = value.ToString();
if (valueString == null)
{
throw new ArgumentNullException(nameof(value), "Enum value cannot be null.");
}
return Enum.Parse(underlyingEnumType, valueString);
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))

View File

@@ -44,12 +44,12 @@ namespace Radzen
/// <summary>
/// Gets or sets a value indicating whether to use secure cookies.
/// </summary>
public bool IsSecure { get; set; } = false;
public bool IsSecure { get; set; }
/// <summary>
/// Gets or sets the SameSite attribute for the cookie.
/// </summary>
public CookieSameSiteMode? SameSite { get; set; } = null;
public CookieSameSiteMode? SameSite { get; set; }
}
/// <summary>
@@ -64,13 +64,16 @@ namespace Radzen
/// <summary>
/// Initializes a new instance of the <see cref="CookieThemeService" /> class.
/// </summary>
public CookieThemeService(IJSRuntime jsRuntime, ThemeService themeService, IOptions<CookieThemeServiceOptions> options)
public CookieThemeService(IJSRuntime jsRuntime, ThemeService themeService, IOptions<CookieThemeServiceOptions>? options)
{
this.jsRuntime = jsRuntime;
this.themeService = themeService;
this.options = options.Value;
this.options = options?.Value ?? new CookieThemeServiceOptions();
themeService.ThemeChanged += OnThemeChanged;
if (themeService != null)
{
themeService.ThemeChanged += OnThemeChanged;
}
_ = InitializeAsync();
}

View File

@@ -47,14 +47,14 @@ namespace Radzen
/// </summary>
/// <value>The name.</value>
[Parameter]
public string Name { get; set; }
public string? Name { get; set; }
/// <summary>
/// Gets or sets the placeholder.
/// </summary>
/// <value>The placeholder.</value>
[Parameter]
public string Placeholder { get; set; }
public string? Placeholder { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="DataBoundFormComponent{T}"/> is disabled.
@@ -80,36 +80,36 @@ namespace Radzen
/// <summary>
/// The form
/// </summary>
IRadzenForm _form;
IRadzenForm? form;
/// <summary>
/// Gets or sets the form.
/// </summary>
/// <value>The form.</value>
[CascadingParameter]
public IRadzenForm Form
public IRadzenForm? Form
{
get
{
return _form;
return form;
}
set
{
_form = value;
_form?.AddComponent(this);
form = value;
form?.AddComponent(this);
}
}
/// <summary>
/// The value
/// </summary>
private T _value = default;
private T? _value;
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
[Parameter]
public T Value
public T? Value
{
get
{
@@ -167,18 +167,18 @@ namespace Radzen
/// </summary>
/// <value>The text property.</value>
[Parameter]
public string TextProperty { get; set; }
public string? TextProperty { get; set; }
/// <summary>
/// The data
/// </summary>
IEnumerable _data = null;
IEnumerable? _data;
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
[Parameter]
public virtual IEnumerable Data
public virtual IEnumerable? Data
{
get
{
@@ -208,7 +208,7 @@ namespace Radzen
/// Gets the query.
/// </summary>
/// <value>The query.</value>
protected virtual IQueryable Query
protected virtual IQueryable? Query
{
get
{
@@ -220,7 +220,7 @@ namespace Radzen
/// Gets or sets the search text
/// </summary>
[Parameter]
public string SearchText
public string? SearchText
{
get
{
@@ -245,29 +245,30 @@ namespace Radzen
/// <summary>
/// The search text
/// </summary>
internal string searchText;
internal string? searchText;
/// <summary>
/// The view
/// </summary>
protected IQueryable _view = null;
protected IQueryable? _view;
/// <summary>
/// Gets the view.
/// </summary>
/// <value>The view.</value>
protected virtual IEnumerable View
protected virtual IEnumerable? View
{
get
{
if (_view == null && Query != null)
var query = Query;
if (_view == null && query != null)
{
if (!string.IsNullOrEmpty(searchText))
if (!string.IsNullOrEmpty(searchText) && !string.IsNullOrEmpty(TextProperty))
{
_view = Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
_view = query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
}
else
{
_view = (typeof(IQueryable).IsAssignableFrom(Data.GetType())) ? Query.Cast<object>().ToList().AsQueryable() : Query;
_view = Data is IQueryable ? query.Cast<object>().ToList().AsQueryable() : query;
}
}
@@ -275,14 +276,14 @@ namespace Radzen
}
}
internal IEnumerable GetView() => View;
internal IEnumerable? GetView() => View;
/// <summary>
/// Gets or sets the edit context.
/// </summary>
/// <value>The edit context.</value>
[CascadingParameter]
public EditContext EditContext { get; set; }
public EditContext? EditContext { get; set; }
/// <summary>
/// Gets the field identifier.
@@ -296,7 +297,7 @@ namespace Radzen
/// </summary>
/// <value>The value expression.</value>
[Parameter]
public Expression<Func<T>> ValueExpression { get; set; }
public Expression<Func<T>>? ValueExpression { get; set; }
/// <summary>
/// Set parameters as an asynchronous operation.
/// </summary>
@@ -338,7 +339,7 @@ namespace Radzen
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="ValidationStateChangedEventArgs"/> instance containing the event data.</param>
private void ValidationStateChanged(object sender, ValidationStateChangedEventArgs e)
private void ValidationStateChanged(object? sender, ValidationStateChangedEventArgs e)
{
StateHasChanged();
}
@@ -356,13 +357,15 @@ namespace Radzen
}
Form?.RemoveComponent(this);
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets the value.
/// </summary>
/// <returns>System.Object.</returns>
public virtual object GetValue()
public virtual object? GetValue()
{
return Value;
}
@@ -386,13 +389,13 @@ namespace Radzen
/// <summary> Provides support for RadzenFormField integration. </summary>
[CascadingParameter]
public IFormFieldContext FormFieldContext { get; set; }
public IFormFieldContext? FormFieldContext { get; set; }
/// <summary> Gets the current placeholder. Returns empty string if this component is inside a RadzenFormField.</summary>
protected string CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
protected string? CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
/// <summary>
/// Handles the <see cref="E:ContextMenu" /> event.
/// Handles the ContextMenu event.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <returns>Task.</returns>

View File

@@ -6,16 +6,16 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.CellContextMenu" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridCellMouseEventArgs<T> : Microsoft.AspNetCore.Components.Web.MouseEventArgs
public class DataGridCellMouseEventArgs<T> : Microsoft.AspNetCore.Components.Web.MouseEventArgs where T : notnull
{
/// <summary>
/// Gets the data item which the clicked DataGrid row represents.
/// </summary>
public T Data { get; internal set; }
public T? Data { get; internal set; }
/// <summary>
/// Gets the RadzenDataGridColumn which this cells represents.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
public RadzenDataGridColumn<T>? Column { get; internal set; }
}

View File

@@ -6,11 +6,11 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.CellRender" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridCellRenderEventArgs<T> : RowRenderEventArgs<T>
public class DataGridCellRenderEventArgs<T> : RowRenderEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the RadzenDataGridColumn which this cells represents.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
public RadzenDataGridColumn<T>? Column { get; internal set; }
}

View File

@@ -11,7 +11,7 @@ internal class DataGridChildData<T>
/// <summary>
/// Gets or sets the parent child data.
/// </summary>
internal DataGridChildData<T> ParentChildData { get; set; }
internal DataGridChildData<T>? ParentChildData { get; set; }
/// <summary>
/// Gets or sets the level.
@@ -21,6 +21,6 @@ internal class DataGridChildData<T>
/// <summary>
/// Gets or sets the data.
/// </summary>
internal IEnumerable<T> Data { get; set; }
internal IEnumerable<T>? Data { get; set; }
}

View File

@@ -6,22 +6,22 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.Filter" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridColumnFilterEventArgs<T>
public class DataGridColumnFilterEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the filtered RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
public RadzenDataGridColumn<T>? Column { get; internal set; }
/// <summary>
/// Gets the new filter value of the filtered column.
/// </summary>
public object FilterValue { get; internal set; }
public object? FilterValue { get; internal set; }
/// <summary>
/// Gets the new second filter value of the filtered column.
/// </summary>
public object SecondFilterValue { get; internal set; }
public object? SecondFilterValue { get; internal set; }
/// <summary>
/// Gets the new filter operator of the filtered column.

View File

@@ -6,16 +6,16 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.Group" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridColumnGroupEventArgs<T>
public class DataGridColumnGroupEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the grouped RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
public RadzenDataGridColumn<T>? Column { get; internal set; }
/// <summary>
/// Gets the group descriptor.
/// </summary>
public GroupDescriptor GroupDescriptor { get; internal set; }
public GroupDescriptor? GroupDescriptor { get; internal set; }
}

View File

@@ -6,12 +6,12 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.ColumnReordered" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridColumnReorderedEventArgs<T>
public class DataGridColumnReorderedEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the reordered RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
public RadzenDataGridColumn<T>? Column { get; internal set; }
/// <summary>
/// Gets the old index of the column.

View File

@@ -6,17 +6,17 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.ColumnReordering" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridColumnReorderingEventArgs<T>
public class DataGridColumnReorderingEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the reordered RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
public RadzenDataGridColumn<T>? Column { get; internal set; }
/// <summary>
/// Gets the reordered to RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> ToColumn { get; internal set; }
public RadzenDataGridColumn<T>? ToColumn { get; internal set; }
/// <summary>
/// Gets or sets a value which will cancel the event.

View File

@@ -6,12 +6,12 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.ColumnResized" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridColumnResizedEventArgs<T>
public class DataGridColumnResizedEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the resized RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
public RadzenDataGridColumn<T>? Column { get; internal set; }
/// <summary>
/// Gets the new width of the resized column.

View File

@@ -8,12 +8,12 @@ public class DataGridColumnSettings
/// <summary>
/// Property.
/// </summary>
public string UniqueID { get; set; }
public string UniqueID { get; set; } = string.Empty;
/// <summary>
/// Property.
/// </summary>
public string Property { get; set; }
public string Property { get; set; } = string.Empty;
/// <summary>
/// Visible.
@@ -23,7 +23,7 @@ public class DataGridColumnSettings
/// <summary>
/// Width.
/// </summary>
public string Width { get; set; }
public string Width { get; set; } = string.Empty;
/// <summary>
/// OrderIndex.
@@ -43,7 +43,7 @@ public class DataGridColumnSettings
/// <summary>
/// FilterValue.
/// </summary>
public object FilterValue { get; set; }
public object? FilterValue { get; set; }
/// <summary>
/// FilterOperator.
@@ -53,7 +53,7 @@ public class DataGridColumnSettings
/// <summary>
/// SecondFilterValue.
/// </summary>
public object SecondFilterValue { get; set; }
public object? SecondFilterValue { get; set; }
/// <summary>
/// SecondFilterOperator.
@@ -68,7 +68,7 @@ public class DataGridColumnSettings
/// <summary>
/// CustomFilterExpression.
/// </summary>
public string CustomFilterExpression { get; set; }
public string CustomFilterExpression { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the mode that determines whether the filter applies to any or all items in a collection.

View File

@@ -6,12 +6,12 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.Sort" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridColumnSortEventArgs<T>
public class DataGridColumnSortEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the sorted RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
public RadzenDataGridColumn<T>? Column { get; internal set; }
/// <summary>
/// Gets the new sort order of the sorted column.

View File

@@ -12,12 +12,12 @@ public class DataGridLoadChildDataEventArgs<T>
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
public IEnumerable<T> Data { get; set; }
public IEnumerable<T>? Data { get; set; }
/// <summary>
/// Gets the item.
/// </summary>
/// <value>The item.</value>
public T Item { get; internal set; }
public T? Item { get; internal set; }
}

View File

@@ -8,13 +8,13 @@ namespace Radzen;
/// Supplies information about a <see cref="Radzen.Blazor.RadzenDataGrid{TItem}.LoadColumnFilterData" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridLoadColumnFilterDataEventArgs<T>
public class DataGridLoadColumnFilterDataEventArgs<T> where T : notnull
{
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
public IEnumerable Data { get; set; }
public IEnumerable? Data { get; set; }
/// <summary>
/// Gets or sets the total data count.
@@ -37,17 +37,17 @@ public class DataGridLoadColumnFilterDataEventArgs<T>
/// Gets the filter expression as a string.
/// </summary>
/// <value>The filter.</value>
public string Filter { get; internal set; }
public string? Filter { get; internal set; }
/// <summary>
/// Gets or sets filter property used to limit and distinct values, if not set, args.Data are used as values.
/// </summary>
/// <value>The filter property.</value>
public string Property { get; set; }
public string? Property { get; set; }
/// <summary>
/// Gets the RadzenDataGridColumn.
/// </summary>
public Radzen.Blazor.RadzenDataGridColumn<T> Column { get; internal set; }
public Radzen.Blazor.RadzenDataGridColumn<T>? Column { get; internal set; }
}

View File

@@ -8,6 +8,6 @@ public class DataGridLoadSettingsEventArgs
/// <summary>
/// Gets or sets the settings.
/// </summary>
public DataGridSettings Settings { get; set; }
public DataGridSettings? Settings { get; set; }
}

View File

@@ -7,11 +7,11 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.PickedColumnsChanged" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridPickedColumnsChangedEventArgs<T>
public class DataGridPickedColumnsChangedEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the picked columns.
/// </summary>
public IEnumerable<RadzenDataGridColumn<T>> Columns { get; internal set; }
public IEnumerable<RadzenDataGridColumn<T>>? Columns { get; internal set; }
}

View File

@@ -6,12 +6,12 @@ namespace Radzen;
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.Render" /> event that is being raised.
/// </summary>
/// <typeparam name="T">The data item type.</typeparam>
public class DataGridRenderEventArgs<T>
public class DataGridRenderEventArgs<T> where T : notnull
{
/// <summary>
/// Gets the instance of the RadzenDataGrid component which has rendered.
/// </summary>
public RadzenDataGrid<T> Grid { get; internal set; }
public RadzenDataGrid<T>? Grid { get; internal set; }
/// <summary>
/// Gets a value indicating whether this is the first time the RadzenDataGrid has rendered.

View File

@@ -9,6 +9,6 @@ public class DataGridRowMouseEventArgs<T> : Microsoft.AspNetCore.Components.Web.
/// <summary>
/// Gets the data item which the clicked DataGrid row represents.
/// </summary>
public T Data { get; internal set; }
public T? Data { get; internal set; }
}

View File

@@ -10,12 +10,12 @@ public class DataGridSettings
/// <summary>
/// Columns.
/// </summary>
public IEnumerable<DataGridColumnSettings> Columns { get; set; }
public IEnumerable<DataGridColumnSettings> Columns { get; set; } = System.Array.Empty<DataGridColumnSettings>();
/// <summary>
/// Groups.
/// </summary>
public IEnumerable<GroupDescriptor> Groups { get; set; }
public IEnumerable<GroupDescriptor> Groups { get; set; } = System.Array.Empty<GroupDescriptor>();
/// <summary>
/// CurrentPage.

View File

@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Linq;
using System.Collections.Generic;
@@ -50,14 +51,14 @@ namespace Radzen.Blazor
{
if (min != null)
{
var minDate = Convert.ToDateTime(min);
var minDate = Convert.ToDateTime(min, CultureInfo.InvariantCulture);
Input.Start = minDate.Ticks;
Round = false;
}
if (max != null)
{
var maxDate = Convert.ToDateTime(max);
var maxDate = Convert.ToDateTime(max, CultureInfo.InvariantCulture);
Input.End = maxDate.Ticks;
Round = false;
}

View File

@@ -6,9 +6,9 @@ namespace Radzen;
/// <summary>
/// Utility class for debouncing and throttling function calls.
/// </summary>
internal class Debouncer
internal class Debouncer : IDisposable
{
private System.Timers.Timer timer;
private System.Timers.Timer? timer;
private DateTime timerStarted { get; set; } = DateTime.UtcNow.AddYears(-1);
/// <summary>
@@ -86,4 +86,15 @@ internal class Debouncer
timer.Start();
timerStarted = curTime;
}
/// <inheritdoc />
public void Dispose()
{
if(timer != null)
{
timer.Stop();
timer.Dispose();
timer = null;
}
}
}

View File

@@ -41,8 +41,8 @@ namespace Radzen
/// </example>
public class DialogService : IDisposable
{
private DotNetObjectReference<DialogService> reference;
internal DotNetObjectReference<DialogService> Reference
private DotNetObjectReference<DialogService>? reference;
internal DotNetObjectReference<DialogService>? Reference
{
get
{
@@ -59,15 +59,15 @@ namespace Radzen
/// Gets or sets the URI helper.
/// </summary>
/// <value>The URI helper.</value>
NavigationManager UriHelper { get; set; }
IJSRuntime JSRuntime { get; set; }
NavigationManager? UriHelper { get; set; }
IJSRuntime? JSRuntime { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="DialogService"/> class.
/// </summary>
/// <param name="uriHelper">The URI helper.</param>
/// <param name="jsRuntime">IJSRuntime instance.</param>
public DialogService(NavigationManager uriHelper, IJSRuntime jsRuntime)
public DialogService(NavigationManager? uriHelper, IJSRuntime? jsRuntime)
{
UriHelper = uriHelper;
JSRuntime = jsRuntime;
@@ -78,9 +78,9 @@ namespace Radzen
}
}
private void UriHelper_OnLocationChanged(object sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
private void UriHelper_OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
{
while (dialogs.Any())
while (dialogs.Count > 0)
{
Close();
}
@@ -94,27 +94,27 @@ namespace Radzen
/// <summary>
/// Raises the Close event.
/// </summary>
public event Action<dynamic> OnClose;
public event Action<dynamic>? OnClose;
/// <summary>
/// Occurs when [on refresh].
/// </summary>
public event Action OnRefresh;
public event Action? OnRefresh;
/// <summary>
/// Occurs when a new dialog is open.
/// </summary>
public event Action<string, Type, Dictionary<string, object>, DialogOptions> OnOpen;
public event Action<string?, Type, Dictionary<string, object>, DialogOptions>? OnOpen;
/// <summary>
/// Raises the Close event for the side dialog
/// </summary>
public event Action<dynamic> OnSideClose;
public event Action<dynamic>? OnSideClose;
/// <summary>
/// Raises the Open event for the side dialog
/// </summary>
public event Action<Type, Dictionary<string, object>, SideDialogOptions> OnSideOpen;
public event Action<Type, Dictionary<string, object>, SideDialogOptions>? OnSideOpen;
/// <summary>
/// Opens a dialog with the specified arguments.
@@ -123,7 +123,7 @@ namespace Radzen
/// <param name="title">The text displayed in the title bar of the dialog.</param>
/// <param name="parameters">The dialog parameters.</param>
/// <param name="options">The dialog options.</param>
public virtual void Open<T>(string title, Dictionary<string, object> parameters = null, DialogOptions options = null) where T : ComponentBase
public virtual void Open<T>(string title, Dictionary<string, object>? parameters = null, DialogOptions? options = null) where T : ComponentBase
{
OpenDialog<T>(title, parameters, options);
}
@@ -135,7 +135,7 @@ namespace Radzen
/// <param name="componentType">The type of the component to be displayed in the dialog. Must inherit from <see cref="ComponentBase"/>.</param>
/// <param name="parameters">The dialog parameters.</param>
/// <param name="options">The dialog options.</param>
public virtual void Open(string title, Type componentType, Dictionary<string, object> parameters = null, DialogOptions options = null)
public virtual void Open(string title, Type componentType, Dictionary<string, object>? parameters = null, DialogOptions? options = null)
{
if (!typeof(ComponentBase).IsAssignableFrom(componentType))
{
@@ -143,8 +143,12 @@ namespace Radzen
}
var method = GetType().GetMethod(nameof(OpenDialog), BindingFlags.Instance | BindingFlags.NonPublic);
if (method == null)
{
throw new InvalidOperationException("OpenDialog method not found.");
}
method.MakeGenericMethod(componentType).Invoke(this, new object[] { title, parameters, options });
method.MakeGenericMethod(componentType).Invoke(this, new object[] { title, parameters!, options! });
}
/// <summary>
@@ -159,7 +163,7 @@ namespace Radzen
/// The tasks
/// </summary>
protected List<TaskCompletionSource<dynamic>> tasks = new List<TaskCompletionSource<dynamic>>();
private TaskCompletionSource<dynamic> sideDialogResultTask;
private TaskCompletionSource<dynamic>? sideDialogResultTask;
/// <summary>
/// Opens a dialog with the specified arguments.
@@ -169,7 +173,7 @@ namespace Radzen
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T" />.</param>
/// <param name="options">The dialog options.</param>
/// <returns>The value passed as argument to <see cref="Close" />.</returns>
public virtual Task<dynamic> OpenAsync<T>(string title, Dictionary<string, object> parameters = null, DialogOptions options = null) where T : ComponentBase
public virtual Task<dynamic> OpenAsync<T>(string title, Dictionary<string, object>? parameters = null, DialogOptions? options = null) where T : ComponentBase
{
var task = new TaskCompletionSource<dynamic>();
tasks.Add(task);
@@ -188,7 +192,7 @@ namespace Radzen
/// <param name="options">The dialog options.</param>
/// <returns>A task that represents the result passed as an argument to <see cref="Close"/>.</returns>
/// <exception cref="ArgumentException">Thrown if <paramref name="componentType"/> does not inherit from <see cref="ComponentBase"/>.</exception>
public virtual Task<dynamic> OpenAsync(string title, Type componentType, Dictionary<string, object> parameters = null, DialogOptions options = null)
public virtual Task<dynamic> OpenAsync(string title, Type componentType, Dictionary<string, object>? parameters = null, DialogOptions? options = null)
{
if (!typeof(ComponentBase).IsAssignableFrom(componentType))
{
@@ -199,8 +203,12 @@ namespace Radzen
tasks.Add(task);
var method = GetType().GetMethod(nameof(OpenDialog), BindingFlags.Instance | BindingFlags.NonPublic);
if (method == null)
{
throw new InvalidOperationException("OpenDialog method not found.");
}
method.MakeGenericMethod(componentType).Invoke(this, new object[] { title, parameters, options });
method.MakeGenericMethod(componentType).Invoke(this, new object[] { title, parameters!, options! });
return task.Task;
}
@@ -214,7 +222,7 @@ namespace Radzen
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T"/></param>
/// <param name="options">The side dialog options.</param>
/// <returns>A task that completes when the dialog is closed or a new one opened</returns>
public Task<dynamic> OpenSideAsync<T>(string title, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
public Task<dynamic> OpenSideAsync<T>(string title, Dictionary<string, object>? parameters = null, SideDialogOptions? options = null)
where T : ComponentBase
{
CloseSide();
@@ -238,7 +246,7 @@ namespace Radzen
/// <param name="options">The side dialog options.</param>
/// <returns>A task that represents the result passed as an argument to <see cref="CloseSide"/>.</returns>
/// <exception cref="ArgumentException">Thrown if <paramref name="componentType"/> does not inherit from <see cref="ComponentBase"/>.</exception>
public Task<dynamic> OpenSideAsync(string title, Type componentType, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
public Task<dynamic> OpenSideAsync(string title, Type componentType, Dictionary<string, object>? parameters = null, SideDialogOptions? options = null)
{
if (!typeof(ComponentBase).IsAssignableFrom(componentType))
{
@@ -267,7 +275,7 @@ namespace Radzen
/// <param name="title">The text displayed in the title bar of the side dialog.</param>
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T"/></param>
/// <param name="options">The side dialog options.</param>
public void OpenSide<T>(string title, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
public void OpenSide<T>(string title, Dictionary<string, object>? parameters = null, SideDialogOptions? options = null)
where T : ComponentBase
{
CloseSide();
@@ -289,7 +297,7 @@ namespace Radzen
/// <param name="parameters">The dialog parameters, passed as property values of the specified component.</param>
/// <param name="options">The side dialog options.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="componentType"/> does not inherit from <see cref="ComponentBase"/>.</exception>
public void OpenSide(string title, Type componentType, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
public void OpenSide(string title, Type componentType, Dictionary<string, object>? parameters = null, SideDialogOptions? options = null)
{
if (!typeof(ComponentBase).IsAssignableFrom(componentType))
{
@@ -312,7 +320,7 @@ namespace Radzen
/// Closes the side dialog
/// </summary>
/// <param name="result">The result of the Dialog</param>
public virtual void CloseSide(dynamic result = null)
public virtual void CloseSide(dynamic? result = null)
{
if (sideDialogResultTask?.Task.IsCompleted == false)
{
@@ -322,7 +330,7 @@ namespace Radzen
OnSideClose?.Invoke(result);
}
private TaskCompletionSource sideDialogCloseTask;
private TaskCompletionSource? sideDialogCloseTask;
internal void OnSideCloseComplete()
{
@@ -334,7 +342,7 @@ namespace Radzen
/// Closes the side dialog and waits for the closing animation to finish.
/// </summary>
/// <param name="result">The result of the Dialog</param>
public async Task CloseSideAsync(dynamic result = null)
public async Task CloseSideAsync(dynamic? result = null)
{
sideDialogCloseTask = new TaskCompletionSource();
@@ -351,7 +359,7 @@ namespace Radzen
/// <param name="options">The dialog options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The value passed as argument to <see cref="Close" />.</returns>
public virtual Task<dynamic> OpenAsync(string title, RenderFragment<DialogService> childContent, DialogOptions options = null, CancellationToken? cancellationToken = null)
public virtual Task<dynamic> OpenAsync(string title, RenderFragment<DialogService> childContent, DialogOptions? options = null, CancellationToken? cancellationToken = null)
{
var task = new TaskCompletionSource<dynamic>();
@@ -377,7 +385,7 @@ namespace Radzen
/// <param name="options">The dialog options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The value passed as argument to <see cref="Close" />.</returns>
public virtual Task<dynamic> OpenAsync(RenderFragment<DialogService> titleContent, RenderFragment<DialogService> childContent, DialogOptions options = null, CancellationToken? cancellationToken = null)
public virtual Task<dynamic> OpenAsync(RenderFragment<DialogService> titleContent, RenderFragment<DialogService> childContent, DialogOptions? options = null, CancellationToken? cancellationToken = null)
{
var task = new TaskCompletionSource<dynamic>();
@@ -402,7 +410,7 @@ namespace Radzen
/// <param name="title">The text displayed in the title bar of the dialog.</param>
/// <param name="childContent">The content displayed in the dialog.</param>
/// <param name="options">The dialog options.</param>
public virtual void Open(string title, RenderFragment<DialogService> childContent, DialogOptions options = null)
public virtual void Open(string title, RenderFragment<DialogService> childContent, DialogOptions? options = null)
{
options = options ?? new DialogOptions();
@@ -416,12 +424,13 @@ namespace Radzen
/// </summary>
protected List<object> dialogs = new List<object>();
internal void OpenDialog<T>(string title, Dictionary<string, object> parameters, DialogOptions options)
internal void OpenDialog<T>(string? title, Dictionary<string, object>? parameters, DialogOptions? options)
{
dialogs.Add(new object());
// Validate and set default values for the dialog options
options ??= new();
parameters ??= new Dictionary<string, object>();
options.Width = !String.IsNullOrEmpty(options.Width) ? options.Width : "600px";
options.Left = !String.IsNullOrEmpty(options.Left) ? options.Left : "";
options.Top = !String.IsNullOrEmpty(options.Top) ? options.Top : "";
@@ -440,7 +449,7 @@ namespace Radzen
/// </summary>
/// <param name="result">The result.</param>
[JSInvokable("DialogService.Close")]
public virtual void Close(dynamic result = null)
public virtual void Close(dynamic? result = null)
{
var dialog = dialogs.LastOrDefault();
@@ -464,7 +473,7 @@ namespace Radzen
reference?.Dispose();
reference = null;
UriHelper.LocationChanged -= UriHelper_OnLocationChanged;
UriHelper?.LocationChanged -= UriHelper_OnLocationChanged;
}
/// <summary>
@@ -475,7 +484,7 @@ namespace Radzen
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><c>true</c> if the user clicked the OK button, <c>false</c> otherwise.</returns>
public virtual async Task<bool?> Confirm(string message = "Confirm?", string title = "Confirm", ConfirmOptions options = null, CancellationToken? cancellationToken = null)
public virtual async Task<bool?> Confirm(string message = "Confirm?", string title = "Confirm", ConfirmOptions? options = null, CancellationToken? cancellationToken = null)
{
// Validate and set default values for the dialog options
options ??= new();
@@ -524,7 +533,7 @@ namespace Radzen
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><c>true</c> if the user clicked the OK button, <c>false</c> otherwise.</returns>
public virtual async Task<bool?> Confirm(RenderFragment message, string title = "Confirm", ConfirmOptions options = null, CancellationToken? cancellationToken = null)
public virtual async Task<bool?> Confirm(RenderFragment message, string title = "Confirm", ConfirmOptions? options = null, CancellationToken? cancellationToken = null)
{
// Validate and set default values for the dialog options
options ??= new();
@@ -573,7 +582,7 @@ namespace Radzen
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><c>true</c> if the user clicked the OK button, <c>false</c> otherwise.</returns>
public virtual async Task<bool?> Alert(string message = "", string title = "Message", AlertOptions options = null, CancellationToken? cancellationToken = null)
public virtual async Task<bool?> Alert(string message = "", string title = "Message", AlertOptions? options = null, CancellationToken? cancellationToken = null)
{
// Validate and set default values for the dialog options
options ??= new();
@@ -616,7 +625,7 @@ namespace Radzen
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><c>true</c> if the user clicked the OK button, <c>false</c> otherwise.</returns>
public virtual async Task<bool?> Alert(RenderFragment message, string title = "Message", AlertOptions options = null, CancellationToken? cancellationToken = null)
public virtual async Task<bool?> Alert(RenderFragment message, string title = "Message", AlertOptions? options = null, CancellationToken? cancellationToken = null)
{
// Validate and set default values for the dialog options
options ??= new();
@@ -660,7 +669,7 @@ namespace Radzen
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Raises the <see cref="PropertyChanged" /> event.
@@ -707,12 +716,12 @@ namespace Radzen
}
}
private string width;
private string? width;
/// <summary>
/// Gets or sets the width of the dialog.
/// </summary>
/// <value>The width.</value>
public string Width
public string? Width
{
get => width;
set
@@ -725,12 +734,12 @@ namespace Radzen
}
}
private string height;
private string? height;
/// <summary>
/// Gets or sets the height of the dialog.
/// </summary>
/// <value>The height.</value>
public string Height
public string? Height
{
get => height;
set
@@ -743,12 +752,12 @@ namespace Radzen
}
}
private string style;
private string? style;
/// <summary>
/// Gets or sets the CSS style of the dialog
/// </summary>
/// <value>The style.</value>
public string Style
public string? Style
{
get => style;
set
@@ -761,7 +770,7 @@ namespace Radzen
}
}
private bool closeDialogOnOverlayClick = false;
private bool closeDialogOnOverlayClick;
/// <summary>
/// Gets or sets a value indicating whether the dialog should be closed by clicking the overlay.
/// </summary>
@@ -779,11 +788,11 @@ namespace Radzen
}
}
private string cssClass;
private string? cssClass;
/// <summary>
/// Gets or sets dialog box custom class
/// </summary>
public string CssClass
public string? CssClass
{
get => cssClass;
set
@@ -796,11 +805,11 @@ namespace Radzen
}
}
private string wrapperCssClass;
private string? wrapperCssClass;
/// <summary>
/// Gets or sets the CSS classes added to the dialog's wrapper element.
/// </summary>
public string WrapperCssClass
public string? WrapperCssClass
{
get => wrapperCssClass;
set
@@ -813,11 +822,11 @@ namespace Radzen
}
}
private string contentCssClass;
private string? contentCssClass;
/// <summary>
/// Gets or sets the CSS classes added to the dialog's content element.
/// </summary>
public string ContentCssClass
public string? ContentCssClass
{
get => contentCssClass;
set
@@ -830,7 +839,7 @@ namespace Radzen
}
}
private int closeTabIndex = 0;
private int closeTabIndex;
/// <summary>
/// Gets or sets a value the dialog escape tabindex. Set to <c>0</c> by default.
/// </summary>
@@ -847,14 +856,14 @@ namespace Radzen
}
}
private RenderFragment<DialogService> titleContent;
private RenderFragment<DialogService>? titleContent;
private bool resizable;
/// <summary>
/// Gets or sets the title content.
/// </summary>
/// <value>The title content.</value>
public RenderFragment<DialogService> TitleContent
public RenderFragment<DialogService>? TitleContent
{
get => titleContent;
set
@@ -890,12 +899,12 @@ namespace Radzen
/// </summary>
public class SideDialogOptions : DialogOptionsBase
{
private string title;
private string? title;
/// <summary>
/// The title displayed on the dialog.
/// </summary>
public string Title
public string? Title
{
get => title;
set
@@ -945,7 +954,7 @@ namespace Radzen
}
}
private bool autoFocusFirstElement = false;
private bool autoFocusFirstElement;
/// <summary>
/// Gets or sets a value indicating whether to focus the first focusable HTML element. Set to <c>true</c> by default.
@@ -1061,26 +1070,26 @@ namespace Radzen
/// </summary>
/// <value>The icon.</value>
public string Icon { get; set; }
public string? Icon { get; set; }
/// <summary>
/// Gets or sets the icon color in Title.
/// </summary>
/// <value>The icon color.</value>
public string IconColor { get; set; }
public string? IconColor { get; set; }
/// <summary>
/// Gets or sets the CSS style of the Icon in Title.
/// </summary>
public string IconStyle { get; set; } = "margin-right: 0.75rem";
private Action<Size> resize;
private Action<Size>? resize;
/// <summary>
/// Gets or sets the change.
/// </summary>
/// <value>The change.</value>
public Action<Size> Resize
public Action<Size>? Resize
{
get => resize;
set
@@ -1112,13 +1121,13 @@ namespace Radzen
}
}
private Action<Point> drag;
private Action<Point>? drag;
/// <summary>
/// Gets or sets the change.
/// </summary>
/// <value>The change.</value>
public Action<Point> Drag
public Action<Point>? Drag
{
get => drag;
set
@@ -1131,13 +1140,13 @@ namespace Radzen
}
}
private string left;
private string? left;
/// <summary>
/// Gets or sets the X coordinate of the dialog. Maps to the <c>left</c> CSS attribute.
/// </summary>
/// <value>The left.</value>
public string Left
public string? Left
{
get => left;
set
@@ -1150,13 +1159,13 @@ namespace Radzen
}
}
private string top;
private string? top;
/// <summary>
/// Gets or sets the Y coordinate of the dialog. Maps to the <c>top</c> CSS attribute.
/// </summary>
/// <value>The top.</value>
public string Top
public string? Top
{
get => top;
set
@@ -1169,13 +1178,13 @@ namespace Radzen
}
}
private string bottom;
private string? bottom;
/// <summary>
/// Specifies the <c>bottom</c> CSS attribute.
/// </summary>
/// <value>The bottom.</value>
public string Bottom
public string? Bottom
{
get => bottom;
set
@@ -1188,13 +1197,13 @@ namespace Radzen
}
}
private RenderFragment<DialogService> childContent;
private RenderFragment<DialogService>? childContent;
/// <summary>
/// Gets or sets the child content.
/// </summary>
/// <value>The child content.</value>
public RenderFragment<DialogService> ChildContent
public RenderFragment<DialogService>? ChildContent
{
get => childContent;
set
@@ -1251,12 +1260,12 @@ namespace Radzen
/// </summary>
public class AlertOptions : DialogOptions
{
private string okButtonText;
private string? okButtonText;
/// <summary>
/// Gets or sets the text of the OK button.
/// </summary>
public string OkButtonText
public string? OkButtonText
{
get => okButtonText;
set
@@ -1275,12 +1284,12 @@ namespace Radzen
/// </summary>
public class ConfirmOptions : AlertOptions
{
private string cancelButtonText;
private string? cancelButtonText;
/// <summary>
/// Gets or sets the text of the Cancel button.
/// </summary>
public string CancelButtonText
public string? CancelButtonText
{
get => cancelButtonText;
set
@@ -1299,13 +1308,13 @@ namespace Radzen
/// </summary>
public class Dialog : INotifyPropertyChanged
{
private string title;
private string? title;
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public string Title
public string? Title
{
get => title;
set
@@ -1318,13 +1327,13 @@ namespace Radzen
}
}
private Type type;
private Type? type;
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public Type Type
public Type? Type
{
get => type;
set
@@ -1337,13 +1346,13 @@ namespace Radzen
}
}
private Dictionary<string, object> parameters;
private Dictionary<string, object>? parameters;
/// <summary>
/// Gets or sets the parameters.
/// </summary>
/// <value>The parameters.</value>
public Dictionary<string, object> Parameters
public Dictionary<string, object>? Parameters
{
get => parameters;
set
@@ -1356,13 +1365,13 @@ namespace Radzen
}
}
private DialogOptions options;
private DialogOptions? options;
/// <summary>
/// Gets or sets the options.
/// </summary>
/// <value>The options.</value>
public DialogOptions Options
public DialogOptions? Options
{
get => options;
set
@@ -1378,7 +1387,7 @@ namespace Radzen
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Raises the <see cref="PropertyChanged"/> event.

View File

@@ -0,0 +1,73 @@
<Project>
<!--
Common build properties for all projects in the Radzen.Blazor solution.
To use this file:
1. Rename to Directory.Build.props (remove .sample extension)
2. Adjust settings based on your needs
3. Review the analyzer settings in .editorconfig
This file will be automatically imported by all projects in subdirectories.
-->
<PropertyGroup Label="Language Configuration">
<!-- Use latest C# language features -->
<LangVersion>latest</LangVersion>
<!-- Do NOT enable implicit usings - explicit imports preferred for library code -->
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoWarn>CA2007</NoWarn>
</PropertyGroup>
<PropertyGroup Label="Code Analysis Configuration">
<!-- Enable .NET code analyzers -->
<AnalysisLevel>latest</AnalysisLevel>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<!-- Run analyzers during build and in IDE -->
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
<!-- Don't enforce code style in build (yet) - just show warnings -->
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
<!-- Don't treat warnings as errors (yet) - too many to fix immediately -->
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<!-- Report all analyzer diagnostics -->
<AnalysisMode>All</AnalysisMode>
</PropertyGroup>
<PropertyGroup Label="Build Quality">
<!-- Enable deterministic builds for reproducibility -->
<Deterministic>true</Deterministic>
<!-- Enable deterministic builds in CI/CD -->
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
<!-- Embed source files for better debugging -->
<EmbedAllSources>true</EmbedAllSources>
<DebugType>embedded</DebugType>
</PropertyGroup>
<PropertyGroup Label="Demos and Tests Project Configuration" Condition="$(MSBuildProjectName.Contains('Demos')) OR $(MSBuildProjectName.Contains('Tests'))">
<!-- Demo projects and Tests should not be packable -->
<IsPackable>false</IsPackable>
<!-- DISABLE ALL ANALYZERS FOR DEMO PROJECTS AND TESTS -->
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
</PropertyGroup>
<PropertyGroup Label="Performance">
<!-- Optimize startup time -->
<TieredCompilation>true</TieredCompilation>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>
</PropertyGroup>
</Project>

View File

@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;
using Radzen.Blazor;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -22,12 +21,12 @@ namespace Radzen
[Parameter]
public int VirtualizationOverscanCount { get; set; }
internal Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<object> virtualize;
internal Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<object>? virtualize;
/// <summary>
/// The Virtualize instance.
/// </summary>
public Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<object> Virtualize
public Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<object>? Virtualize
{
get
{
@@ -35,12 +34,12 @@ namespace Radzen
}
}
List<object> virtualItems;
List<object>? virtualItems;
private async ValueTask<Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<object>> LoadItems(Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderRequest request)
{
var data = Data != null ? Data.Cast<object>() : Enumerable.Empty<object>();
var view = (LoadData.HasDelegate ? data : View).Cast<object>().AsQueryable();
var view = (LoadData.HasDelegate ? data : View)?.Cast<object>().AsQueryable() ?? Enumerable.Empty<object>().AsQueryable();
var totalItemsCount = LoadData.HasDelegate ? Count : view.Count();
var top = request.Count;
@@ -54,7 +53,7 @@ namespace Radzen
await LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = request.StartIndex, Top = top, Filter = searchText });
}
virtualItems = (LoadData.HasDelegate ? Data : view.Skip(request.StartIndex).Take(top)).Cast<object>().ToList();
virtualItems = (LoadData.HasDelegate ? (Data ?? Enumerable.Empty<object>()) : view.Skip(request.StartIndex).Take(top)).Cast<object>().ToList();
return new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<object>(virtualItems, LoadData.HasDelegate ? Count : totalItemsCount);
}
@@ -105,13 +104,13 @@ namespace Radzen
builder.AddAttribute(1, "ItemsProvider", new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderDelegate<object>(LoadItems));
builder.AddAttribute(2, "ChildContent", (RenderFragment<object>)((context) =>
{
return (RenderFragment)((b) =>
return b =>
{
RenderItem(b, context);
});
};
}));
if (VirtualizationOverscanCount != default(int))
if (VirtualizationOverscanCount != default)
{
builder.AddAttribute(3, "OverscanCount", VirtualizationOverscanCount);
}
@@ -122,9 +121,13 @@ namespace Radzen
}
else
{
foreach (var item in LoadData.HasDelegate ? Data : View)
var items = LoadData.HasDelegate ? Data : View;
if (items != null)
{
RenderItem(builder, item);
foreach (var item in items)
{
RenderItem(builder, item);
}
}
}
});
@@ -160,21 +163,18 @@ namespace Radzen
//
}
System.Collections.Generic.HashSet<object> keys = new System.Collections.Generic.HashSet<object>();
HashSet<object> keys = new HashSet<object>();
internal object GetKey(object item)
internal object? GetKey(object item)
{
var value = GetItemOrValueFromProperty(item, ValueProperty);
var value = GetItemOrValueFromProperty(item, ValueProperty ?? string.Empty);
if (!keys.Contains(value))
if (value != null)
{
keys.Add(value);
return value;
}
else
{
return item;
}
return value;
}
/// <summary>
@@ -182,7 +182,7 @@ namespace Radzen
/// </summary>
/// <value>The header template.</value>
[Parameter]
public RenderFragment HeaderTemplate { get; set; }
public RenderFragment? HeaderTemplate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether filtering is allowed. Set to <c>false</c> by default.
@@ -231,21 +231,21 @@ namespace Radzen
/// </summary>
/// <value>The template.</value>
[Parameter]
public RenderFragment<dynamic> Template { get; set; }
public RenderFragment<dynamic>? Template { get; set; }
/// <summary>
/// Gets or sets the value property.
/// </summary>
/// <value>The value property.</value>
[Parameter]
public string ValueProperty { get; set; }
public string? ValueProperty { get; set; }
/// <summary>
/// Gets or sets the disabled property.
/// </summary>
/// <value>The disabled property.</value>
[Parameter]
public string DisabledProperty { get; set; }
public string? DisabledProperty { get; set; }
/// <summary>
/// Gets or sets the remove chip button title.
@@ -282,7 +282,7 @@ namespace Radzen
/// <summary>
/// The selected item
/// </summary>
protected object selectedItem = null;
protected object? selectedItem;
Type GetItemType(IEnumerable items)
{
var firstType = items.Cast<object>().FirstOrDefault()?.GetType() ?? typeof(object);
@@ -301,7 +301,7 @@ namespace Radzen
/// </summary>
protected virtual async System.Threading.Tasks.Task SelectAll()
{
if (Disabled)
if (Disabled || View == null)
{
return;
}
@@ -316,11 +316,14 @@ namespace Radzen
selectedItems.Clear();
}
if (!string.IsNullOrEmpty(ValueProperty))
if (!string.IsNullOrEmpty(ValueProperty) && Data != null)
{
var elementType = PropertyAccess.GetElementType(Data.GetType());
System.Reflection.PropertyInfo pi = PropertyAccess.GetProperty(elementType, ValueProperty);
internalValue = selectedItems.Select(i => GetItemOrValueFromProperty(i, ValueProperty)).AsQueryable().Cast(pi.PropertyType);
System.Reflection.PropertyInfo? pi = PropertyAccess.GetProperty(elementType, ValueProperty);
if (pi != null)
{
internalValue = selectedItems.Select(i => GetItemOrValueFromProperty(i, ValueProperty)).AsQueryable().Cast(pi.PropertyType);
}
}
else
{
@@ -329,25 +332,34 @@ namespace Radzen
internalValue = selectedItems.AsQueryable().Cast(type);
}
await collectionAssignment.MakeAssignment((IEnumerable)internalValue, ValueChanged);
if (internalValue != null)
{
await collectionAssignment.MakeAssignment((IEnumerable)internalValue, ValueChanged);
}
if (FieldIdentifier.FieldName != null) { EditContext?.NotifyFieldChanged(FieldIdentifier); }
await Change.InvokeAsync(internalValue);
StateHasChanged();
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", GetId());
if (JSRuntime != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", GetId());
}
}
internal bool IsAllSelected()
{
List<object> notDisabledItemsInList = View.Cast<object>().ToList()
List<object> notDisabledItemsInList = View != null ? View.Cast<object>().ToList()
.Where(i => disabledPropertyGetter == null || disabledPropertyGetter(i) as bool? != true)
.ToList();
.ToList() : new List<object>();
if (LoadData.HasDelegate && !string.IsNullOrEmpty(ValueProperty))
{
return View != null && notDisabledItemsInList.Count > 0 && notDisabledItemsInList
.All(i => IsItemSelectedByValue(GetItemOrValueFromProperty(i, ValueProperty)));
.All(i => {
var value = GetItemOrValueFromProperty(i, ValueProperty);
return value != null ? IsItemSelectedByValue(value) : false;
});
}
return View != null && notDisabledItemsInList.Count > 0 && selectedItems.Count == notDisabledItemsInList.Count;
@@ -365,14 +377,17 @@ namespace Radzen
/// <summary>
/// Clears all.
/// </summary>
protected async System.Threading.Tasks.Task ClearAll()
protected async Task ClearAll()
{
if (Disabled)
return;
searchText = null;
await SearchTextChanged.InvokeAsync(searchText);
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, "");
if (JSRuntime != null)
{
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, "");
}
internalValue = collectionAssignment.GetCleared();
selectedItem = null;
@@ -381,7 +396,7 @@ namespace Radzen
selectedIndex = -1;
await ValueChanged.InvokeAsync((T)internalValue);
await ValueChanged.InvokeAsync(internalValue != null ? (T)internalValue : default(T)!);
if (FieldIdentifier.FieldName != null) { EditContext?.NotifyFieldChanged(FieldIdentifier); }
await Change.InvokeAsync(internalValue);
@@ -393,14 +408,14 @@ namespace Radzen
/// <summary>
/// The data
/// </summary>
IEnumerable _data;
IEnumerable? _data;
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
[Parameter]
public override IEnumerable Data
public override IEnumerable? Data
{
get
{
@@ -466,22 +481,26 @@ namespace Radzen
}
}
Func<object, object> GetGetter(string propertyName, Type type)
Func<object, object?> GetGetter(string propertyName, Type type)
{
if (propertyName?.Contains("[") == true)
if (propertyName?.Contains('[', StringComparison.Ordinal) == true)
{
var getter = typeof(PropertyAccess).GetMethod("Getter", [typeof(string), typeof(Type)]);
if (getter == null)
{
return PropertyAccess.Getter<object, object?>(propertyName, type);
}
var getterMethod = getter.MakeGenericMethod([type, typeof(object)]);
return (i) => getterMethod.Invoke(i, [propertyName, type]);
return (i) => getterMethod.Invoke(i, [propertyName, type])!;
}
return PropertyAccess.Getter<object, object>(propertyName, type);
return PropertyAccess.Getter<object, object?>(propertyName ?? string.Empty, type);
}
internal Func<object, object> valuePropertyGetter;
internal Func<object, object> textPropertyGetter;
internal Func<object, object> disabledPropertyGetter;
internal Func<object, object?>? valuePropertyGetter;
internal Func<object, object?>? textPropertyGetter;
internal Func<object, object?>? disabledPropertyGetter;
/// <summary>
/// Gets the item or value from property.
@@ -489,7 +508,7 @@ namespace Radzen
/// <param name="item">The item.</param>
/// <param name="property">The property.</param>
/// <returns>System.Object.</returns>
public object GetItemOrValueFromProperty(object item, string property)
public object? GetItemOrValueFromProperty(object? item, string property)
{
if (item != null)
{
@@ -610,16 +629,19 @@ namespace Radzen
if (Disabled)
return;
await JSRuntime.InvokeVoidAsync("Radzen.togglePopup", Element, PopupID, true);
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", isFilter ? UniqueID : SearchID);
if (JSRuntime != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.togglePopup", Element, PopupID, true);
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", isFilter ? UniqueID : SearchID);
}
if (list != null)
if (list != null && JSRuntime != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.selectListItem", search, list, selectedIndex);
}
}
internal bool preventKeydown = false;
internal bool preventKeydown;
/// <summary>
/// Handles the key press.
@@ -627,9 +649,9 @@ namespace Radzen
/// <param name="args">The <see cref="Microsoft.AspNetCore.Components.Web.KeyboardEventArgs"/> instance containing the event data.</param>
/// <param name="isFilter">if set to <c>true</c> [is filter].</param>
/// <param name="shouldSelectOnChange">Should select item on item change with keyboard.</param>
protected virtual async System.Threading.Tasks.Task HandleKeyPress(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs args, bool isFilter = false, bool? shouldSelectOnChange = null)
protected virtual async Task HandleKeyPress(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs args, bool isFilter = false, bool? shouldSelectOnChange = null)
{
if (Disabled || Data == null)
if (Disabled || Data == null || args == null)
return;
List<object> items = Enumerable.Empty<object>().ToList();
@@ -649,7 +671,7 @@ namespace Radzen
}
else
{
items = View.Cast<object>().ToList();
items = View != null ? View.Cast<object>().ToList() : Enumerable.Empty<object>().ToList();
}
}
@@ -661,16 +683,19 @@ namespace Radzen
try
{
selectedIndex = await JSRuntime.InvokeAsync<int>("Radzen.focusListItem", search, list, key == "ArrowDown" || key == "ArrowRight", selectedIndex);
var popupOpened = await JSRuntime.InvokeAsync<bool>("Radzen.popupOpened", PopupID);
if (!Multiple && !popupOpened && shouldSelectOnChange != false)
if (JSRuntime != null)
{
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
if (itemToSelect != null)
selectedIndex = await JSRuntime.InvokeAsync<int>("Radzen.focusListItem", search, list, key == "ArrowDown" || key == "ArrowRight", selectedIndex);
var popupOpened = await JSRuntime.InvokeAsync<bool>("Radzen.popupOpened", PopupID);
if (!Multiple && !popupOpened && shouldSelectOnChange != false)
{
await OnSelectItem(itemToSelect, true);
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
if (itemToSelect != null)
{
await OnSelectItem(itemToSelect, true);
}
}
}
}
@@ -683,37 +708,43 @@ namespace Radzen
{
preventKeydown = true;
if (selectedIndex == -1 && items.Count() == 1)
if (selectedIndex == -1 && items.Count == 1)
{
selectedIndex = 0;
}
if (selectedIndex >= 0 && selectedIndex <= items.Count() - 1)
if (JSRuntime != null)
{
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, $"{searchText}".Trim());
if (itemToSelect != null)
if (selectedIndex >= 0 && selectedIndex <= items.Count - 1)
{
await OnSelectItem(itemToSelect, true);
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, $"{searchText}".Trim());
if (itemToSelect != null)
{
await OnSelectItem(itemToSelect, true);
}
}
}
var popupOpened = await JSRuntime.InvokeAsync<bool>("Radzen.popupOpened", PopupID);
var popupOpened = await JSRuntime.InvokeAsync<bool>("Radzen.popupOpened", PopupID);
if (!popupOpened)
{
if(key != "Space")
if (!popupOpened)
{
await OpenPopup(key, isFilter);
if (key != "Space")
{
await OpenPopup(key, isFilter);
}
}
}
else
{
if (!Multiple && (!isFilter || key != "Space"))
else
{
await ClosePopup(key);
if (!Multiple && (!isFilter || key != "Space"))
{
if (!Multiple && !isFilter)
{
await ClosePopup(key);
}
}
}
}
}
@@ -758,9 +789,20 @@ namespace Radzen
else if (args.Key.Length == 1 && !args.CtrlKey && !args.AltKey && !args.ShiftKey)
{
// searching for element
var filteredItems = Query.Where(TextProperty, args.Key, StringFilterOperator.StartsWith, FilterCaseSensitivity.CaseInsensitive)
.Cast(Query.ElementType).Cast<dynamic>().ToList();
if (Query == null)
{
return;
}
var query = Query;
var elementType = query.ElementType;
if (elementType == null)
{
return;
}
var filteredItems = (!string.IsNullOrEmpty(TextProperty) ?
query.Where(TextProperty, args.Key, StringFilterOperator.StartsWith, FilterCaseSensitivity.CaseInsensitive) :
query)
.Cast(elementType).Cast<dynamic>().ToList();
if (previousKey != args.Key)
{
@@ -768,7 +810,7 @@ namespace Radzen
itemIndex = -1;
}
itemIndex = itemIndex + 1 >= filteredItems.Count() ? 0 : itemIndex + 1;
itemIndex = itemIndex + 1 >= filteredItems.Count ? 0 : itemIndex + 1;
var itemToSelect = filteredItems.ElementAtOrDefault(itemIndex);
if (itemToSelect is not null)
@@ -785,7 +827,10 @@ namespace Radzen
{
selectedIndex = result.Index;
}
await JSRuntime.InvokeVoidAsync("Radzen.selectListItem", list, list, result.Index);
if (JSRuntime != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.selectListItem", list, list, result.Index);
}
}
}
@@ -795,14 +840,17 @@ namespace Radzen
internal virtual async Task ClosePopup(string key)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
if (JSRuntime != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
}
}
int itemIndex;
string previousKey;
string? previousKey;
/// <summary>
/// Handles the <see cref="E:FilterKeyPress" /> event.
/// Handles the FilterKeyPress event.
/// </summary>
/// <param name="args">The <see cref="Microsoft.AspNetCore.Components.Web.KeyboardEventArgs"/> instance containing the event data.</param>
protected virtual async System.Threading.Tasks.Task OnFilterKeyPress(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs args)
@@ -852,13 +900,15 @@ namespace Radzen
if (Multiple)
selectedIndex = -1;
await JSRuntime.InvokeAsync<string>("Radzen.repositionPopup", Element, PopupID);
if (JSRuntime != null)
{
await JSRuntime.InvokeAsync<string>("Radzen.repositionPopup", Element, PopupID);
}
await InvokeAsync(() => SearchTextChanged.InvokeAsync(SearchText));
}
/// <summary>
/// Handles the <see cref="E:KeyPress" /> event.
/// Handles the KeyPress event.
/// </summary>
/// <param name="args">The <see cref="Microsoft.AspNetCore.Components.Web.KeyboardEventArgs"/> instance containing the event data.</param>
/// <param name="shouldSelectOnChange">Should select item on item change with keyboard.</param>
@@ -872,13 +922,13 @@ namespace Radzen
/// </summary>
/// <param name="item">The item.</param>
/// <param name="isFromKey">if set to <c>true</c> [is from key].</param>
protected virtual async System.Threading.Tasks.Task OnSelectItem(object item, bool isFromKey = false)
protected virtual async System.Threading.Tasks.Task OnSelectItem(object? item, bool isFromKey = false)
{
await SelectItem(item);
}
/// <summary>
/// Handles the <see cref="E:Filter" /> event.
/// Handles the Filter event.
/// </summary>
/// <param name="args">The <see cref="ChangeEventArgs"/> instance containing the event data.</param>
protected virtual async System.Threading.Tasks.Task OnFilter(ChangeEventArgs args)
@@ -946,7 +996,7 @@ namespace Radzen
if (valueChanged)
{
internalValue = parameters.GetValueOrDefault<object>(nameof(Value));
if (PreserveCollectionOnSelection)
if (PreserveCollectionOnSelection && internalValue != null)
{
collectionAssignment = new ReferenceGenericCollectionAssignment((T)internalValue);
}
@@ -969,7 +1019,7 @@ namespace Radzen
}
var shouldClose = visibleChanged && !Visible;
if (shouldClose && !firstRender)
if (shouldClose && !firstRender && JSRuntime != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);
}
@@ -991,18 +1041,24 @@ namespace Radzen
}
}
SelectItemFromValue(internalValue);
if (internalValue != null)
{
SelectItemFromValue(internalValue);
}
return base.OnParametersSetAsync();
}
/// <summary>
/// Handles the <see cref="E:Change" /> event.
/// Handles the Change event.
/// </summary>
/// <param name="args">The <see cref="ChangeEventArgs"/> instance containing the event data.</param>
protected void OnChange(ChangeEventArgs args)
{
internalValue = args.Value;
if (args != null)
{
internalValue = args.Value;
}
}
/// <summary>
@@ -1014,7 +1070,8 @@ namespace Radzen
{
if (!string.IsNullOrEmpty(ValueProperty))
{
return IsItemSelectedByValue(GetItemOrValueFromProperty(item, ValueProperty));
var value = GetItemOrValueFromProperty(item, ValueProperty);
return value != null ? IsItemSelectedByValue(value) : false;
}
else
{
@@ -1034,7 +1091,7 @@ namespace Radzen
/// </summary>
/// <value>The selected item.</value>
[Parameter]
public object SelectedItem
public object? SelectedItem
{
get
{
@@ -1072,13 +1129,13 @@ namespace Radzen
/// Gets the view.
/// </summary>
/// <value>The view.</value>
protected override IEnumerable View
protected override IEnumerable? View
{
get
{
if (_view == null && Query != null)
{
_view = Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
_view = Query.Where(TextProperty ?? string.Empty, searchText, FilterOperator, FilterCaseSensitivity);
}
return _view;
@@ -1090,7 +1147,7 @@ namespace Radzen
/// </summary>
void SetSelectedIndexFromSelectedItem()
{
if (selectedItem != null)
if (selectedItem != null && View != null)
{
if (typeof(EnumerableQuery).IsAssignableFrom(View.GetType()))
{
@@ -1112,17 +1169,17 @@ namespace Radzen
/// </summary>
/// <param name="item">The item.</param>
/// <param name="raiseChange">if set to <c>true</c> [raise change].</param>
internal async System.Threading.Tasks.Task SelectItemInternal(object item, bool raiseChange = true)
internal async Task SelectItemInternal(object item, bool raiseChange = true)
{
await SelectItem(item, raiseChange);
}
internal object internalValue;
internal object? internalValue;
/// <summary>
/// Will add/remove selected items from a bound ICollection&lt;T&gt;, instead of replacing it.
/// </summary>
protected bool PreserveCollectionOnSelection = false;
protected bool PreserveCollectionOnSelection;
private DefaultCollectionAssignment collectionAssignment = new();
/// <summary>
@@ -1130,7 +1187,7 @@ namespace Radzen
/// </summary>
/// <param name="item">The item.</param>
/// <param name="raiseChange">if set to <c>true</c> [raise change].</param>
public async System.Threading.Tasks.Task SelectItem(object item, bool raiseChange = true)
public async Task SelectItem(object? item, bool raiseChange = true)
{
if (disabledPropertyGetter != null && item != null && disabledPropertyGetter(item) as bool? == true)
{
@@ -1143,7 +1200,7 @@ namespace Radzen
return;
selectedItem = item;
if (!string.IsNullOrEmpty(ValueProperty))
if (!string.IsNullOrEmpty(ValueProperty) && item != null)
{
internalValue = PropertyAccess.GetItemOrValueFromProperty(item, ValueProperty);
}
@@ -1158,16 +1215,26 @@ namespace Radzen
}
else
{
UpdateSelectedItems(item);
if (item != null)
{
UpdateSelectedItems(item);
}
if (!string.IsNullOrEmpty(ValueProperty))
if (!string.IsNullOrEmpty(ValueProperty) && Data != null)
{
var elementType = PropertyAccess.GetElementType(Data.GetType());
System.Reflection.PropertyInfo pi = PropertyAccess.GetProperty(elementType, ValueProperty);
internalValue = selectedItems.Select(i => GetItemOrValueFromProperty(i, ValueProperty)).AsQueryable().Cast(pi.PropertyType);
System.Reflection.PropertyInfo? pi = PropertyAccess.GetProperty(elementType, ValueProperty);
if(pi != null)
{
internalValue = selectedItems.Select(i => GetItemOrValueFromProperty(i, ValueProperty)).AsQueryable().Cast(pi.PropertyType);
}
}
else
{
if (Data == null)
{
return;
}
var query = Data.AsQueryable();
var elementType = query.ElementType;
@@ -1196,11 +1263,14 @@ namespace Radzen
{
if (Multiple)
{
await collectionAssignment.MakeAssignment((IEnumerable)internalValue, ValueChanged);
if (internalValue != null)
{
await collectionAssignment.MakeAssignment((IEnumerable)internalValue, ValueChanged);
}
}
else
{
await ValueChanged.InvokeAsync(internalValue != null ? (T)internalValue : default);
await ValueChanged.InvokeAsync(internalValue != null ? (T)internalValue : default(T)!);
}
}
@@ -1212,7 +1282,7 @@ namespace Radzen
}
/// <inheritdoc />
public override object GetValue()
public override object? GetValue()
{
return internalValue;
}
@@ -1223,7 +1293,7 @@ namespace Radzen
{
var value = GetItemOrValueFromProperty(item, ValueProperty);
if (!IsItemSelectedByValue(value))
if (value != null && !IsItemSelectedByValue(value))
{
selectedItems.Add(item);
}
@@ -1292,7 +1362,7 @@ namespace Radzen
if (typeof(EnumerableQuery).IsAssignableFrom(view.GetType()))
{
item = view.OfType<object>().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), v)).FirstOrDefault();
item = view.OfType<object>().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), v)).FirstOrDefault()!;
}
else
{
@@ -1305,7 +1375,7 @@ namespace Radzen
}
},
LogicalFilterOperator.And,
FilterCaseSensitivity.Default).FirstOrDefault();
FilterCaseSensitivity.Default).FirstOrDefault()!;
}
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), v)).Any())
@@ -1330,7 +1400,7 @@ namespace Radzen
/// <summary>
/// For lists of objects, an IEqualityComparer to control how selected items are determined
/// </summary>
[Parameter] public IEqualityComparer<object> ItemComparer { get; set; }
[Parameter] public IEqualityComparer<object>? ItemComparer { get; set; }
internal bool IsItemSelectedByValue(object v)
{
@@ -1353,6 +1423,8 @@ namespace Radzen
base.Dispose();
keys.Clear();
GC.SuppressFinalize(this);
}
private class DefaultCollectionAssignment
@@ -1363,42 +1435,55 @@ namespace Radzen
{
if (object.Equals(selectedItems, null))
{
await valueChanged.InvokeAsync(default(T));
await valueChanged.InvokeAsync(default(T)!);
}
else
{
var list = (IList)Activator.CreateInstance(typeof(T));
foreach (var i in (IEnumerable)selectedItems)
var list = (IList?)Activator.CreateInstance<T>();
if (list != null)
{
list.Add(i);
foreach (var i in (IEnumerable)selectedItems)
{
list.Add(i);
}
await valueChanged.InvokeAsync((T)(object)list);
}
else
{
await valueChanged.InvokeAsync(default(T)!);
}
await valueChanged.InvokeAsync((T)(object)list);
}
}
else if (typeof(T).IsGenericType && typeof(ICollection<>).MakeGenericType(typeof(T).GetGenericArguments()[0]).IsAssignableFrom(typeof(T)))
{
if (object.Equals(selectedItems, null))
{
await valueChanged.InvokeAsync(default(T));
await valueChanged.InvokeAsync(default(T)!);
}
else
{
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T).GetGenericArguments()[0]));
var list = (IList?)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T).GetGenericArguments()[0]));
if (list != null)
{
foreach (var i in (IEnumerable)selectedItems)
{
list.Add(i);
}
await valueChanged.InvokeAsync((T)(object)list);
}
else
{
await valueChanged.InvokeAsync(default(T)!);
}
}
}
else
{
await valueChanged.InvokeAsync(object.Equals(selectedItems, null) ? default(T) : (T)selectedItems);
await valueChanged.InvokeAsync(object.Equals(selectedItems, null) ? default(T)! : (T)selectedItems);
}
}
public virtual T GetCleared()
public virtual T? GetCleared()
{
return default(T);
}
@@ -1408,9 +1493,9 @@ namespace Radzen
{
private readonly T originalCollection;
private readonly bool canHandle;
private readonly System.Reflection.MethodInfo clearMethod;
private readonly System.Reflection.MethodInfo addMethod;
private readonly System.Reflection.MethodInfo removeMethod;
private readonly System.Reflection.MethodInfo? clearMethod;
private readonly System.Reflection.MethodInfo? addMethod;
private readonly System.Reflection.MethodInfo? removeMethod;
public ReferenceGenericCollectionAssignment(T originalCollection)
{
@@ -1437,34 +1522,34 @@ namespace Radzen
public override async Task MakeAssignment(IEnumerable selectedItems, EventCallback<T> valueChanged)
{
if (!canHandle)
if (!canHandle || originalCollection == null)
{
// Fallback to default behavior when we can't handle the type
// Fallback to default behavior when we can't handle the type or originalCollection is null
await base.MakeAssignment(selectedItems, valueChanged);
return;
}
var currentItems = selectedItems.Cast<object>().ToHashSet();
var existingItems = ((IEnumerable)originalCollection).Cast<object>().ToHashSet();
var existingItems = (originalCollection as IEnumerable)?.Cast<object>().ToHashSet() ?? new HashSet<object>();
foreach (var i in currentItems)
{
if (!existingItems.Contains(i))
addMethod.Invoke(originalCollection, [i]);
addMethod!.Invoke(originalCollection, [i]);
}
foreach (var i in existingItems)
{
if (!currentItems.Contains(i))
removeMethod.Invoke(originalCollection, [i]);
removeMethod!.Invoke(originalCollection, [i]);
}
await valueChanged.InvokeAsync(originalCollection);
}
public override T GetCleared()
public override T? GetCleared()
{
if (canHandle)
if (canHandle && originalCollection != null)
{
clearMethod.Invoke(originalCollection, null);
clearMethod!.Invoke(originalCollection, null);
return originalCollection;
}
return base.GetCleared();

View File

@@ -10,7 +10,7 @@ public class DropDownBaseItemRenderEventArgs<TValue>
/// <summary>
/// Gets the data item.
/// </summary>
public object Item { get; internal set; }
public object? Item { get; internal set; }
/// <summary>
/// Gets or sets a value indicating whether this item is visible.

View File

@@ -10,6 +10,6 @@ public class DropDownItemRenderEventArgs<TValue> : DropDownBaseItemRenderEventAr
/// <summary>
/// Gets the DropDown.
/// </summary>
public RadzenDropDown<TValue> DropDown { get; internal set; }
public RadzenDropDown<TValue>? DropDown { get; internal set; }
}

View File

@@ -9,8 +9,13 @@ namespace System.Linq.Dynamic.Core
/// </summary>
public static class DynamicExtensions
{
static readonly Func<string, Type> typeLocator = type => AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName.Replace("+", ".") == type);
static readonly Func<string, Type?> typeLocator = type => AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.FirstOrDefault(t =>
{
var fullName = t.FullName;
return fullName != null && fullName.Replace("+", ".", StringComparison.Ordinal) == type;
});
/// <summary>
/// Filters using the specified filter descriptors.
@@ -18,28 +23,30 @@ namespace System.Linq.Dynamic.Core
public static IQueryable<T> Where<T>(
this IQueryable<T> source,
string predicate,
object[] parameters = null, object[] otherParameters = null)
object[]? parameters = null, object[]? otherParameters = null)
{
ArgumentNullException.ThrowIfNull(source);
try
{
if (parameters != null && !string.IsNullOrEmpty(predicate))
{
predicate = Regex.Replace(predicate, @"@(\d+)", match =>
{
int index = int.Parse(match.Groups[1].Value);
int index = int.Parse(match.Groups[1].Value, System.Globalization.CultureInfo.InvariantCulture);
if (index >= parameters.Length)
throw new InvalidOperationException($"No parameter provided for {match.Value}");
return ExpressionSerializer.FormatValue(parameters[index]);
return ExpressionSerializer.FormatValue(parameters[index]) ?? string.Empty;
});
}
predicate = (predicate == "true" ? "" : predicate)
.Replace("DateTime(", "DateTime.Parse(")
.Replace("DateTimeOffset(", "DateTimeOffset.Parse(")
.Replace("DateOnly(", "DateOnly.Parse(")
.Replace("Guid(", "Guid.Parse(")
.Replace(" = ", " == ");
predicate = (predicate == "true" ? "" : predicate ?? string.Empty)
.Replace("DateTime(", "DateTime.Parse(", StringComparison.Ordinal)
.Replace("DateTimeOffset(", "DateTimeOffset.Parse(", StringComparison.Ordinal)
.Replace("DateOnly(", "DateOnly.Parse(", StringComparison.Ordinal)
.Replace("Guid(", "Guid.Parse(", StringComparison.Ordinal)
.Replace(" = ", " == ", StringComparison.Ordinal);
return !string.IsNullOrEmpty(predicate) ?
source.Where(ExpressionParser.ParsePredicate<T>(predicate, typeLocator)) : source;
@@ -56,8 +63,10 @@ namespace System.Linq.Dynamic.Core
public static IOrderedQueryable<T> OrderBy<T>(
this IQueryable<T> source,
string selector,
object[] parameters = null)
object[]? parameters = null)
{
ArgumentNullException.ThrowIfNull(source);
try
{
return QueryableExtension.OrderBy(source, selector);
@@ -71,8 +80,10 @@ namespace System.Linq.Dynamic.Core
/// <summary>
/// Projects each element of a sequence into a collection of property values.
/// </summary>
public static IQueryable Select<T>(this IQueryable<T> source, string selector, object[] parameters = null)
public static IQueryable Select<T>(this IQueryable<T> source, string selector, object[]? parameters = null)
{
ArgumentNullException.ThrowIfNull(source);
if (source.ElementType == typeof(object))
{
var elementType = source.ElementType;
@@ -95,8 +106,10 @@ namespace System.Linq.Dynamic.Core
/// <summary>
/// Projects each element of a sequence into a collection of property values.
/// </summary>
public static IQueryable Select(this IQueryable source, string selector, object[] parameters = null)
public static IQueryable Select(this IQueryable source, string selector, object[]? parameters = null)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(selector);
return source.Select(selector, expression => ExpressionParser.ParseLambda(expression, source.ElementType));
}
@@ -109,18 +122,27 @@ namespace System.Linq.Dynamic.Core
return source;
}
if (!selector.Contains("=>"))
if (!selector.Contains("=>", StringComparison.Ordinal))
{
var properties = selector
.Replace("new (", "").Replace(")", "").Replace("new {", "").Replace("}", "").Trim()
.Replace("new (", "", StringComparison.Ordinal).Replace(")", "", StringComparison.Ordinal).Replace("new {", "", StringComparison.Ordinal).Replace("}", "", StringComparison.Ordinal).Trim()
.Split(",", StringSplitOptions.RemoveEmptyEntries);
selector = string.Join(", ", properties
.Select(s => (s.Contains(" as ") ? s.Split(" as ").LastOrDefault().Trim().Replace(".", "_") : s.Trim().Replace(".", "_")) +
" = " + $"it.{s.Split(" as ").FirstOrDefault().Replace(".", "?.").Trim()}"));
.Select(s =>
{
var parts = s.Split(" as ", StringSplitOptions.RemoveEmptyEntries);
var sourcePart = (parts.FirstOrDefault() ?? s).Trim();
var targetPart = (parts.Length > 1 ? parts.Last() : sourcePart).Trim();
var safeTarget = targetPart.Replace(".", "_", StringComparison.Ordinal);
var safeSource = sourcePart.Replace(".", "?.", StringComparison.Ordinal);
return $"{safeTarget} = it.{safeSource}";
}));
}
var lambda = lambdaCreator(selector.Contains("=>") ? selector : $"it => new {{ {selector} }}");
var lambda = lambdaCreator(selector.Contains("=>", StringComparison.Ordinal) ? selector : $"it => new {{ {selector} }}");
return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), nameof(Queryable.Select),
[source.ElementType, lambda.Body.Type], source.Expression, Expression.Quote(lambda)));

View File

@@ -39,7 +39,7 @@ static class DynamicTypeFactory
"set_" + propertyNames[i],
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
[propertyTypes[i]]);
new[] { propertyTypes[i] });
var setterIl = setterMethod.GetILGenerator();
setterIl.Emit(OpCodes.Ldarg_0);
@@ -51,6 +51,10 @@ static class DynamicTypeFactory
}
var dynamicType = typeBuilder.CreateType();
if (dynamicType == null)
{
throw new InvalidOperationException("Failed to create dynamic type.");
}
return dynamicType;
}
}

View File

@@ -28,7 +28,7 @@ public class ExpressionParser
/// </summary>
public static Expression<Func<T, TResult>> ParseLambda<T, TResult>(string expression, Func<string, Type?>? typeResolver = null)
{
var lambda = ParseLambda(expression, typeof(T), typeResolver);
var lambda = ParseLambda<T>(expression, typeResolver);
return Expression.Lambda<Func<T, TResult>>(lambda.Body, lambda.Parameters[0]);
}
@@ -52,7 +52,7 @@ public class ExpressionParser
}
private readonly List<Token> tokens;
private int position = 0;
private int position;
private readonly Func<string, Type?>? typeResolver;
private readonly Stack<ParameterExpression> parameterStack = new();

View File

@@ -30,6 +30,7 @@ public class ExpressionSerializer : ExpressionVisitor
/// <inheritdoc/>
protected override Expression VisitLambda<T>(Expression<T> node)
{
ArgumentNullException.ThrowIfNull(node);
if (node.Parameters.Count > 1)
{
_sb.Append("(");
@@ -52,6 +53,7 @@ public class ExpressionSerializer : ExpressionVisitor
/// <inheritdoc/>
protected override Expression VisitParameter(ParameterExpression node)
{
ArgumentNullException.ThrowIfNull(node);
_sb.Append(node.Name);
return node;
}
@@ -59,10 +61,11 @@ public class ExpressionSerializer : ExpressionVisitor
/// <inheritdoc/>
protected override Expression VisitMember(MemberExpression node)
{
ArgumentNullException.ThrowIfNull(node);
if (node.Expression != null)
{
Visit(node.Expression);
_sb.Append($".{node.Member.Name}");
_sb.Append(CultureInfo.InvariantCulture, $".{node.Member.Name}");
}
else
{
@@ -74,14 +77,15 @@ public class ExpressionSerializer : ExpressionVisitor
/// <inheritdoc/>
protected override Expression VisitMethodCall(MethodCallExpression node)
{
ArgumentNullException.ThrowIfNull(node);
if (node.Method.IsStatic && node.Arguments.Count > 0 &&
(node.Method.DeclaringType == typeof(Enumerable) ||
(node.Method.DeclaringType == typeof(Enumerable) ||
node.Method.DeclaringType == typeof(Queryable)))
{
Visit(node.Arguments[0]);
_sb.Append($".{node.Method.Name}(");
_sb.Append(CultureInfo.InvariantCulture, $".{node.Method.Name}(");
for (int i = 1; i < node.Arguments.Count; i++)
for (int i = 1; i < node.Arguments.Count; i++)
{
if (i > 1) _sb.Append(", ");
@@ -99,7 +103,7 @@ public class ExpressionSerializer : ExpressionVisitor
}
else if (node.Method.IsStatic)
{
_sb.Append($"{node.Method.DeclaringType.Name}.{node.Method.Name}(");
_sb.Append(CultureInfo.InvariantCulture, $"{node.Method.DeclaringType?.Name}.{node.Method.Name}(");
for (int i = 0; i < node.Arguments.Count; i++)
{
@@ -114,11 +118,11 @@ public class ExpressionSerializer : ExpressionVisitor
if (node.Object != null)
{
Visit(node.Object);
_sb.Append($".{node.Method.Name}(");
_sb.Append(CultureInfo.InvariantCulture, $".{node.Method.Name}(");
}
else
{
_sb.Append($"{node.Method.Name}(");
_sb.Append(CultureInfo.InvariantCulture, $"{node.Method.Name}(");
}
for (int i = 0; i < node.Arguments.Count; i++)
@@ -136,17 +140,18 @@ public class ExpressionSerializer : ExpressionVisitor
/// <inheritdoc/>
protected override Expression VisitUnary(UnaryExpression node)
{
ArgumentNullException.ThrowIfNull(node);
if (node.NodeType == ExpressionType.Not)
{
_sb.Append("(!(");
Visit(node.Operand);
_sb.Append("))");
}
else if (node.NodeType == ExpressionType.Convert)
else if (node.NodeType == ExpressionType.Convert)
{
if (node.Operand is IndexExpression indexExpr)
{
_sb.Append($"({node.Type.DisplayName(true).Replace("+",".")})");
_sb.Append(CultureInfo.InvariantCulture, $"({node.Type.DisplayName(true).Replace("+", ".", StringComparison.Ordinal)})");
Visit(indexExpr.Object);
@@ -175,20 +180,18 @@ public class ExpressionSerializer : ExpressionVisitor
/// <inheritdoc/>
protected override Expression VisitConstant(ConstantExpression node)
{
ArgumentNullException.ThrowIfNull(node);
_sb.Append(FormatValue(node.Value));
return node;
}
internal static string FormatValue(object value)
internal static string? FormatValue(object? value)
{
if (value == null)
return "null";
return value switch
{
string s when s == string.Empty => @"""""",
string s when s.Length == 0 => @"""""",
null => "null",
string s => @$"""{s.Replace("\"", "\\\"")}""",
string s => @$"""{s.Replace("\"", "\\\"", StringComparison.Ordinal)}""",
char c => $"'{c}'",
bool b => b.ToString().ToLowerInvariant(),
DateTime dt => FormatDateTime(dt),
@@ -198,7 +201,7 @@ public class ExpressionSerializer : ExpressionVisitor
Guid guid => $"Guid.Parse(\"{guid.ToString("D", CultureInfo.InvariantCulture)}\")",
IEnumerable enumerable when value is not string => FormatEnumerable(enumerable),
_ => value.GetType().IsEnum
? $"({value.GetType().FullName.Replace("+", ".")})" + Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture).ToString()
? $"({value.GetType()?.FullName?.Replace("+", ".", StringComparison.Ordinal)})" + Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture).ToString()
: Convert.ToString(value, CultureInfo.InvariantCulture)
};
}
@@ -214,14 +217,15 @@ public class ExpressionSerializer : ExpressionVisitor
private static string FormatEnumerable(IEnumerable enumerable)
{
var arrayType = enumerable.AsQueryable().ElementType;
var items = enumerable.Cast<object>().Select(FormatValue);
return $"new {(Nullable.GetUnderlyingType(arrayType) != null ? arrayType.DisplayName(true).Replace("+", ".") : "")}[] {{ {string.Join(", ", items)} }}";
return $"new {(Nullable.GetUnderlyingType(arrayType) != null ? arrayType.DisplayName(true).Replace("+", ".", StringComparison.Ordinal) : "")}[] {{ {string.Join(", ", items)} }}";
}
/// <inheritdoc/>
protected override Expression VisitNewArray(NewArrayExpression node)
{
ArgumentNullException.ThrowIfNull(node);
bool needsParentheses = node.NodeType == ExpressionType.NewArrayInit &&
(node.Expressions.Count > 1 || node.Expressions[0].NodeType != ExpressionType.Constant);
@@ -245,9 +249,10 @@ public class ExpressionSerializer : ExpressionVisitor
/// <inheritdoc/>
protected override Expression VisitBinary(BinaryExpression node)
{
ArgumentNullException.ThrowIfNull(node);
_sb.Append("(");
Visit(node.Left);
_sb.Append($" {GetOperator(node.NodeType)} ");
_sb.Append(CultureInfo.InvariantCulture, $" {GetOperator(node.NodeType)} ");
Visit(node.Right);
_sb.Append(")");
return node;
@@ -256,6 +261,7 @@ public class ExpressionSerializer : ExpressionVisitor
/// <inheritdoc/>
protected override Expression VisitConditional(ConditionalExpression node)
{
ArgumentNullException.ThrowIfNull(node);
_sb.Append("(");
Visit(node.Test);
_sb.Append(" ? ");
@@ -290,7 +296,7 @@ public class ExpressionSerializer : ExpressionVisitor
ExpressionType.Coalesce => "??",
_ => throw new NotSupportedException($"Unsupported operator: {type}")
};
}
}
}
/// <summary>
@@ -333,6 +339,7 @@ public static class SharedTypeExtensions
/// <returns>A string representing the type name.</returns>
public static string DisplayName(this Type type, bool fullName = true, bool compilable = false)
{
ArgumentNullException.ThrowIfNull(type);
var stringBuilder = new StringBuilder();
ProcessType(stringBuilder, type, fullName, compilable);
return stringBuilder.ToString();
@@ -443,7 +450,7 @@ public static class SharedTypeExtensions
}
}
var genericPartIndex = type.Name.IndexOf('`');
var genericPartIndex = type.Name.IndexOf('`', StringComparison.Ordinal);
if (genericPartIndex <= 0)
{
builder.Append(type.Name);

View File

@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@@ -18,8 +19,9 @@ namespace Radzen.Blazor
/// <summary>
/// Gets enum description.
/// </summary>
public static string GetDisplayDescription(this Enum enumValue, Func<string, string> translationFunction = null)
public static string GetDisplayDescription(this Enum enumValue, Func<string, string>? translationFunction = null)
{
ArgumentNullException.ThrowIfNull(enumValue);
var enumValueAsString = enumValue.ToString();
var val = enumValue.GetType().GetMember(enumValueAsString).FirstOrDefault();
var enumVal = val?.GetCustomAttribute<DisplayAttribute>()?.GetDescription() ?? enumValueAsString;
@@ -33,10 +35,12 @@ namespace Radzen.Blazor
/// <summary>
/// Converts Enum to IEnumerable of Value/Text.
/// </summary>
public static IEnumerable<object> EnumAsKeyValuePair(Type enumType, Func<string, string> translationFunction = null)
public static IEnumerable<object> EnumAsKeyValuePair(Type enumType, Func<string, string>? translationFunction = null)
{
ArgumentNullException.ThrowIfNull(enumType);
Type underlyingType = Enum.GetUnderlyingType(enumType);
return Enum.GetValues(enumType).Cast<Enum>().Distinct().Select(val => new { Value = Convert.ChangeType(val, underlyingType), Text = val.GetDisplayDescription(translationFunction) });
return Enum.GetValues(enumType).Cast<Enum>().Distinct().Select(val => new { Value = Convert.ChangeType(val, underlyingType, CultureInfo.InvariantCulture), Text = val.GetDisplayDescription(translationFunction) });
}
/// <summary>
@@ -67,7 +71,7 @@ namespace Radzen.Blazor
var value = typeValue.ToString();
value = Regex.Replace(value, "([^A-Z])([A-Z])", "$1-$2");
return Regex.Replace(value, "([A-Z]+)([A-Z][^A-Z$])", "$1-$2")
.Trim().ToLower();
.Trim().ToLowerInvariant();
}
}
}

View File

@@ -17,7 +17,7 @@ public class FileInfo : IBrowserFile
//
}
private IBrowserFile source;
private IBrowserFile? source;
/// <summary>
/// Creates FileInfo with IBrowserFile as source.
@@ -28,7 +28,9 @@ public class FileInfo : IBrowserFile
this.source = source;
}
private string name;
private string? name;
private DateTimeOffset? lastModified;
private string? contentType;
/// <summary>
/// Gets the name of the selected file.
@@ -37,7 +39,7 @@ public class FileInfo : IBrowserFile
{
get
{
return name ?? source.Name;
return name ?? source?.Name ?? string.Empty;
}
set
{
@@ -65,17 +67,25 @@ public class FileInfo : IBrowserFile
/// <summary>
/// Gets the IBrowserFile source.
/// </summary>
public IBrowserFile Source => source;
public IBrowserFile? Source => source;
/// <summary>
/// Gets the LastModified.
/// </summary>
public DateTimeOffset LastModified => source.LastModified;
public DateTimeOffset LastModified
{
get => lastModified ?? source?.LastModified ?? default;
set => lastModified = value;
}
/// <summary>
/// Gets the ContentType.
/// </summary>
public string ContentType => source.ContentType;
public string ContentType
{
get => contentType ?? source?.ContentType ?? string.Empty;
set => contentType = value;
}
/// <summary>
/// Open read stream.
@@ -85,6 +95,11 @@ public class FileInfo : IBrowserFile
/// <returns>The stream.</returns>
public System.IO.Stream OpenReadStream(long maxAllowedSize = 512000, CancellationToken cancellationToken = default)
{
if (source == null)
{
throw new InvalidOperationException("No underlying browser file is associated with this FileInfo instance.");
}
return source.OpenReadStream(maxAllowedSize, cancellationToken);
}
}

View File

@@ -12,26 +12,26 @@ public class FilterDescriptor
/// Gets or sets the name of the filtered property.
/// </summary>
/// <value>The property.</value>
public string Property { get; set; }
public string? Property { get; set; }
/// <summary>
/// Gets or sets the property type.
/// </summary>
/// <value>The property type.</value>
[JsonIgnore]
public Type Type { get; set; }
public Type? Type { get; set; }
/// <summary>
/// Gets or sets the name of the filtered property.
/// </summary>
/// <value>The property.</value>
public string FilterProperty { get; set; }
public string? FilterProperty { get; set; }
/// <summary>
/// Gets or sets the value to filter by.
/// </summary>
/// <value>The filter value.</value>
public object FilterValue { get; set; }
public object? FilterValue { get; set; }
/// <summary>
/// Gets or sets the operator which will compare the property value with <see cref="FilterValue" />.
@@ -43,7 +43,7 @@ public class FilterDescriptor
/// Gets or sets a second value to filter by.
/// </summary>
/// <value>The second filter value.</value>
public object SecondFilterValue { get; set; }
public object? SecondFilterValue { get; set; }
/// <summary>
/// Gets or sets the operator which will compare the property value with <see cref="SecondFilterValue" />.

View File

@@ -39,8 +39,30 @@ namespace Radzen
/// AutoCompleteType.</value>
public virtual string AutoCompleteAttribute
{
get => Attributes != null && Attributes.ContainsKey("AutoComplete") && $"{Attributes["AutoComplete"]}".ToLower() == "false" ? DefaultAutoCompleteAttribute :
Attributes != null && Attributes.ContainsKey("AutoComplete") ? Attributes["AutoComplete"] as string ?? AutoCompleteType.GetAutoCompleteValue() : AutoCompleteType.GetAutoCompleteValue();
get
{
if (Attributes != null && Attributes.TryGetValue("AutoComplete", out var value))
{
var v = (object?)value;
var autoCompleteValue = v switch
{
bool boolValue => boolValue
? AutoCompleteType.GetAutoCompleteValue()
: DefaultAutoCompleteAttribute,
string stringValue when bool.TryParse(stringValue, out var boolValue) => boolValue
? AutoCompleteType.GetAutoCompleteValue()
: DefaultAutoCompleteAttribute,
AutoCompleteType typeValue => typeValue.GetAutoCompleteValue(),
_ => value != null ? value.ToString() ?? AutoCompleteType.GetAutoCompleteValue() : AutoCompleteType.GetAutoCompleteValue()
};
return string.Equals(autoCompleteValue, "false", StringComparison.OrdinalIgnoreCase)
? DefaultAutoCompleteAttribute
: autoCompleteValue;
}
return AutoCompleteType.GetAutoCompleteValue();
}
}
/// <summary>
@@ -48,7 +70,7 @@ namespace Radzen
/// </summary>
public virtual string DefaultAutoCompleteAttribute { get; set; } = "off";
object ariaAutoComplete;
object? ariaAutoComplete;
/// <inheritdoc />
public override async Task SetParametersAsync(ParameterView parameters)
@@ -56,10 +78,10 @@ namespace Radzen
parameters = parameters.TryGetValue("aria-autocomplete", out ariaAutoComplete) ?
ParameterView.FromDictionary(parameters
.ToDictionary().Where(i => i.Key != "aria-autocomplete").ToDictionary(i => i.Key, i => i.Value)
.ToDictionary(i => i.Key, i => i.Value))
.ToDictionary(i => i.Key, i => (object?)i.Value))
: parameters;
await base.SetParametersAsync(parameters);
await base.SetParametersAsync(parameters).ConfigureAwait(false);
}
/// <summary>
@@ -70,9 +92,11 @@ namespace Radzen
/// <summary>
/// Gets the aria-autocomplete attribute's string value.
/// </summary>
public virtual string AriaAutoCompleteAttribute
public virtual string? AriaAutoCompleteAttribute
{
get => AutoCompleteAttribute == DefaultAutoCompleteAttribute ? DefaultAriaAutoCompleteAttribute : ariaAutoComplete as string;
get => string.Equals(AutoCompleteAttribute, DefaultAutoCompleteAttribute, StringComparison.Ordinal)
? DefaultAriaAutoCompleteAttribute
: ariaAutoComplete as string;
}
}
@@ -96,7 +120,7 @@ namespace Radzen
/// </summary>
/// <value>The component name.</value>
[Parameter]
public string Name { get; set; }
public string? Name { get; set; }
/// <summary>
/// Gets or sets the tab order index for keyboard navigation.
@@ -112,7 +136,7 @@ namespace Radzen
/// </summary>
/// <value>The placeholder.</value>
[Parameter]
public string Placeholder { get; set; }
public string? Placeholder { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="FormComponent{T}"/> is disabled.
@@ -124,21 +148,21 @@ namespace Radzen
/// <summary>
/// The form
/// </summary>
IRadzenForm _form;
IRadzenForm? _form;
/// <summary>
/// Gets or sets the edit context.
/// </summary>
/// <value>The edit context.</value>
[CascadingParameter]
public EditContext EditContext { get; set; }
public EditContext? EditContext { get; set; }
/// <summary>
/// Gets or sets the form.
/// </summary>
/// <value>The form.</value>
[CascadingParameter]
public IRadzenForm Form
public IRadzenForm? Form
{
get
{
@@ -189,14 +213,14 @@ namespace Radzen
/// <summary>
/// The value
/// </summary>
protected T _value;
protected T? _value;
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
[Parameter]
public virtual T Value
public virtual T? Value
{
get
{
@@ -230,7 +254,7 @@ namespace Radzen
/// </summary>
/// <value>The value expression.</value>
[Parameter]
public Expression<Func<T>> ValueExpression { get; set; }
public Expression<Func<T>>? ValueExpression { get; set; }
/// <summary>
/// Sets the parameters asynchronous.
/// </summary>
@@ -262,7 +286,7 @@ namespace Radzen
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="ValidationStateChangedEventArgs"/> instance containing the event data.</param>
private void ValidationStateChanged(object sender, ValidationStateChangedEventArgs e)
private void ValidationStateChanged(object? sender, ValidationStateChangedEventArgs e)
{
StateHasChanged();
}
@@ -280,19 +304,21 @@ namespace Radzen
}
Form?.RemoveComponent(this);
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets the value.
/// </summary>
/// <returns>System.Object.</returns>
public object GetValue()
public object? GetValue()
{
return Value;
}
/// <summary>
/// Handles the <see cref="E:ContextMenu" /> event.
/// Handles the ContextMenu event.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <returns>Task.</returns>
@@ -318,10 +344,10 @@ namespace Radzen
/// <summary> Provides support for RadzenFormField integration. </summary>
[CascadingParameter]
public IFormFieldContext FormFieldContext { get; set; }
public IFormFieldContext? FormFieldContext { get; set; }
/// <summary> Gets the current placeholder. Returns empty string if this component is inside a RadzenFormField.</summary>
protected string CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
protected string? CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
/// <inheritdoc/>
public virtual async ValueTask FocusAsync()

View File

@@ -10,6 +10,6 @@ public class FormInvalidSubmitEventArgs
/// <summary>
/// Gets the validation errors.
/// </summary>
public IEnumerable<string> Errors { get; set; }
public IEnumerable<string>? Errors { get; set; }
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
@@ -18,7 +19,7 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The child content.</value>
[Parameter]
public RenderFragment ChildContent
public RenderFragment? ChildContent
{
get; set;
}
@@ -38,7 +39,7 @@ namespace Radzen.Blazor
/// <summary>
/// The width and height are set
/// </summary>
bool widthAndHeightAreSet = false;
bool widthAndHeightAreSet;
/// <summary>
/// The first render
/// </summary>
@@ -57,7 +58,7 @@ namespace Radzen.Blazor
{
visibleChanged = false;
if (Visible)
if (Visible && JSRuntime != null)
{
var rect = await JSRuntime.InvokeAsync<Rect>("Radzen.createGauge", Element, Reference);
@@ -117,23 +118,20 @@ namespace Radzen.Blazor
double width = 0;
double height = 0;
if (CurrentStyle.ContainsKey("height"))
if (CurrentStyle.TryGetValue("height", out var pixelHeight))
{
var pixelHeight = CurrentStyle["height"];
if (pixelHeight.EndsWith("px"))
if (pixelHeight.EndsWith("px", StringComparison.Ordinal))
{
height = Convert.ToDouble(pixelHeight.TrimEnd("px".ToCharArray()));
height = Convert.ToDouble(pixelHeight.TrimEnd("px".ToCharArray()), CultureInfo.InvariantCulture);
}
}
if (CurrentStyle.ContainsKey("width"))
if (CurrentStyle.TryGetValue("width", out var pixelWidth))
{
var pixelWidth = CurrentStyle["width"];
if (pixelWidth.EndsWith("px"))
if (pixelWidth.EndsWith("px", StringComparison.Ordinal))
{
width = Convert.ToDouble(pixelWidth.TrimEnd("px".ToCharArray()));
width = Convert.ToDouble(pixelWidth.TrimEnd("px".ToCharArray()), CultureInfo.InvariantCulture);
}
}
@@ -149,7 +147,7 @@ namespace Radzen.Blazor
/// <summary>
/// The visible changed
/// </summary>
private bool visibleChanged = false;
private bool visibleChanged;
/// <summary>
/// Set parameters as an asynchronous operation.
@@ -166,7 +164,7 @@ namespace Radzen.Blazor
if (visibleChanged && !firstRender)
{
if (Visible == false)
if (Visible == false && JSRuntime != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.destroyGauge", Element);
}
@@ -185,10 +183,12 @@ namespace Radzen.Blazor
{
base.Dispose();
if (Visible)
if (Visible && JSRuntime != null)
{
JSRuntime.InvokeVoid("Radzen.destroyGauge", Element);
}
GC.SuppressFinalize(this);
}
/// <summary>

View File

@@ -8,6 +8,6 @@ public class GoogleMapClickEventArgs
/// <summary>
/// The position which represents the clicked map location.
/// </summary>
public GoogleMapPosition Position { get; set; }
public GoogleMapPosition? Position { get; set; }
}

View File

@@ -20,7 +20,7 @@ public class GoogleMapPosition : IEquatable<GoogleMapPosition>
public double Lng { get; set; }
/// <inheritdoc />
public bool Equals(GoogleMapPosition other)
public bool Equals(GoogleMapPosition? other)
{
if (other != null)
{
@@ -31,7 +31,7 @@ public class GoogleMapPosition : IEquatable<GoogleMapPosition>
}
/// <inheritdoc />
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return this.Equals(obj as GoogleMapPosition);
}

View File

@@ -9,13 +9,13 @@ public class Group
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
public GroupResult Data { get; set; }
public GroupResult Data { get; set; } = new GroupResult();
/// <summary>
/// Gets or sets the group descriptor.
/// </summary>
/// <value>The group descriptor.</value>
public GroupDescriptor GroupDescriptor { get; set; }
public GroupDescriptor? GroupDescriptor { get; set; }
/// <summary>
/// Gets or sets the level.

View File

@@ -9,7 +9,7 @@ public class GroupDescriptor
/// Gets or sets the property to group by.
/// </summary>
/// <value>The property.</value>
public string Property { get; set; }
public string Property { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the sort order.
@@ -21,13 +21,13 @@ public class GroupDescriptor
/// Gets or sets the title displayed in the group.
/// </summary>
/// <value>The title.</value>
public string Title { get; set; }
public string Title { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the format string used to display the key in the group.
/// </summary>
/// <value>The format string.</value>
public string FormatString { get; set; }
public string FormatString { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether to show the footer for the group.

View File

@@ -22,12 +22,12 @@ public class GroupResult
/// <summary>
/// The resulting elements in the group.
/// </summary>
public IEnumerable Items { get; internal set; }
public IEnumerable? Items { get; internal set; }
/// <summary>
/// The resulting subgroups in the group.
/// </summary>
public IEnumerable<GroupResult> Subgroups { get; internal set; }
public IEnumerable<GroupResult>? Subgroups { get; internal set; }
/// <summary>
/// Returns a <see cref="System.String" /> showing the key of the group and the number of items in the group.

View File

@@ -15,7 +15,7 @@ public class GroupRowRenderEventArgs
/// <summary>
/// Gets the data item which the current row represents.
/// </summary>
public Group Group { get; internal set; }
public Group? Group { get; internal set; }
/// <summary>
/// Gets or sets a value indicating whether this group row is expanded.

View File

@@ -24,6 +24,6 @@ public class HtmlEditorExecuteEventArgs
/// <summary>
/// Gets the name of the command which RadzenHtmlEditor is executing.
/// </summary>
public string CommandName { get; set; }
public string? CommandName { get; set; }
}

View File

@@ -9,6 +9,6 @@ public class HtmlEditorPasteEventArgs
/// Gets or sets the HTML content that is pasted in RadzenHtmlEditor. Use the setter to filter unwanted markup from the pasted value.
/// </summary>
/// <value>The HTML.</value>
public string Html { get; set; }
public string? Html { get; set; }
}

View File

@@ -22,8 +22,9 @@ namespace Radzen
/// <exception cref="Exception"></exception>
/// <exception cref="Exception">Unable to parse the response.</exception>
/// <exception cref="Exception"></exception>
public static async Task<T> ReadAsync<T>(this HttpResponseMessage response)
public static async Task<T?> ReadAsync<T>(this HttpResponseMessage response)
{
ArgumentNullException.ThrowIfNull(response);
try
{
response.EnsureSuccessStatusCode();
@@ -42,7 +43,8 @@ namespace Radzen
var responseAsString = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(responseAsString))
{
if (response.Content.Headers.ContentType.MediaType == "application/json")
var mediaType = response.Content.Headers.ContentType?.MediaType;
if (string.Equals(mediaType, "application/json", StringComparison.OrdinalIgnoreCase))
{
JsonDocument json;
try
@@ -51,7 +53,7 @@ namespace Radzen
}
catch
{
throw new Exception("Unable to parse the response.");
throw new InvalidOperationException("Unable to parse the response.");
}
JsonElement error;
@@ -60,13 +62,14 @@ namespace Radzen
JsonElement message;
if (error.TryGetProperty("message", out message))
{
throw new Exception(message.GetString());
var messageText = message.GetString();
throw new InvalidOperationException(messageText ?? "An error occurred.");
}
}
}
else
{
XElement error = null;
XElement? error = null;
try
{
var xml = XDocument.Parse(responseAsString);
@@ -82,12 +85,12 @@ namespace Radzen
}
catch
{
throw new Exception("Unable to parse the response.");
throw new InvalidOperationException("Unable to parse the response.");
}
if (error != null)
{
throw new Exception(error.Value);
throw new InvalidOperationException(error.Value);
}
}
}

View File

@@ -23,14 +23,14 @@ public interface IAIChatService
/// <param name="apiKey">Optional API key to override the configured API key.</param>
/// <param name="apiKeyHeader">Optional API key header name to override the configured header.</param>
/// <returns>An async enumerable that yields streaming response chunks from the AI model.</returns>
IAsyncEnumerable<string> GetCompletionsAsync(string userInput, string sessionId = null, CancellationToken cancellationToken = default, string model = null, string systemPrompt = null, double? temperature = null, int? maxTokens = null, string endpoint = null, string proxy = null, string apiKey = null, string apiKeyHeader = null);
IAsyncEnumerable<string> GetCompletionsAsync(string userInput, string? sessionId = null, CancellationToken cancellationToken = default, string? model = null, string? systemPrompt = null, double? temperature = null, int? maxTokens = null, string? endpoint = null, string? proxy = null, string? apiKey = null, string? apiKeyHeader = null);
/// <summary>
/// Gets or creates a conversation session.
/// </summary>
/// <param name="sessionId">The session ID. If null, a new session will be created.</param>
/// <returns>The conversation session.</returns>
ConversationSession GetOrCreateSession(string sessionId = null);
ConversationSession GetOrCreateSession(string? sessionId = null);
/// <summary>
/// Clears the conversation history for a specific session.

View File

@@ -25,13 +25,13 @@ public interface IRadzenFormComponent
/// Gets the value of the component.
/// </summary>
/// <returns>the value of the component - for example the text of RadzenTextBox.</returns>
object GetValue();
object? GetValue();
/// <summary>
/// Gets or sets the name of the component.
/// </summary>
/// <value>The name.</value>
string Name { get; set; }
string? Name { get; set; }
/// <summary>
/// Gets the field identifier.
@@ -58,6 +58,6 @@ public interface IRadzenFormComponent
/// <summary>
/// Sets the FormFieldContext of the component
/// </summary>
IFormFieldContext FormFieldContext { get; }
IFormFieldContext? FormFieldContext { get; }
}

View File

@@ -10,13 +10,13 @@ public class LegendClickEventArgs
/// <summary>
/// Gets the data at the clicked location.
/// </summary>
public object Data { get; set; }
public object? Data { get; set; }
/// <summary>
/// Gets the title of the clicked series. Determined by <see cref="CartesianSeries{TItem}.Title" />.
/// </summary>
/// <value>The title.</value>
public string Title { get; set; }
public string? Title { get; set; }
/// <summary>
/// Gets the visibility of the clicked legend. Determined by <see cref="CartesianSeries{TItem}.IsVisible" />. Always visible for Pie Charts.

View File

@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Linq;
using System.Collections.Generic;
@@ -20,10 +21,10 @@ namespace Radzen.Blazor
if (String.IsNullOrEmpty(format))
{
return value.ToString();
return value.ToString() ?? String.Empty;
}
return string.Format(format, value);
return string.Format(CultureInfo.InvariantCulture, format, value);
}
public override double Scale(double value, bool padding)
@@ -96,7 +97,7 @@ namespace Radzen.Blazor
{
if (Step is IConvertible)
{
step = Convert.ToDouble(Step);
step = Convert.ToDouble(Step, CultureInfo.InvariantCulture);
}
}
@@ -124,7 +125,7 @@ namespace Radzen.Blazor
if (step == 0)
{
throw new ArgumentOutOfRangeException("Step must be non-zero");
throw new ArgumentOutOfRangeException(nameof(distance), "Step must be non-zero");
}
return (start, end, Math.Abs(step));

View File

@@ -10,6 +10,6 @@ public class ListBoxItemRenderEventArgs<TValue> : DropDownBaseItemRenderEventArg
/// <summary>
/// Gets the ListBox.
/// </summary>
public RadzenListBox<TValue> ListBox { get; internal set; }
public RadzenListBox<TValue>? ListBox { get; internal set; }
}

View File

@@ -23,11 +23,11 @@ public class LoadDataArgs
/// <summary>
/// Gets the sort expression as a string.
/// </summary>
public string OrderBy { get; set; }
public string? OrderBy { get; set; }
private Func<string> getFilter;
private Func<string>? getFilter;
internal Func<string> GetFilter
internal Func<string>? GetFilter
{
get
{
@@ -40,19 +40,19 @@ public class LoadDataArgs
}
}
private string filter;
private string? filter;
/// <summary>
/// Gets the filter expression as a string.
/// </summary>
/// <value>The filter.</value>
public string Filter
public string? Filter
{
get
{
if (filter == null && GetFilter != null)
{
filter = GetFilter();
filter = GetFilter?.Invoke();
}
return filter;
}
@@ -65,12 +65,12 @@ public class LoadDataArgs
/// <summary>
/// Gets the filter expression as a collection of filter descriptors.
/// </summary>
public IEnumerable<FilterDescriptor> Filters { get; set; }
public IEnumerable<FilterDescriptor>? Filters { get; set; }
/// <summary>
/// Gets the sort expression as a collection of sort descriptors.
/// </summary>
/// <value>The sorts.</value>
public IEnumerable<SortDescriptor> Sorts { get; set; }
public IEnumerable<SortDescriptor>? Sorts { get; set; }
}

View File

@@ -8,12 +8,12 @@ public class LoginArgs
/// <summary>
/// Gets or sets the username.
/// </summary>
public string Username { get; set; }
public string? Username { get; set; }
/// <summary>
/// Gets or sets the password.
/// </summary>
public string Password { get; set; }
public string? Password { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the user wants to remember their credentials.

View File

@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Linq;
namespace Radzen;
@@ -59,6 +60,8 @@ public class MD5
/// <returns>The MD5 hash as a string.</returns>
public static string Calculate(byte[] input)
{
ArgumentNullException.ThrowIfNull(input);
uint a0 = 0x67452301; // A
uint b0 = 0xefcdab89; // B
uint c0 = 0x98badcfe; // C
@@ -124,7 +127,7 @@ public class MD5
private static string GetByteString(uint x)
{
return String.Join("", BitConverter.GetBytes(x).Select(y => y.ToString("x2")));
return String.Join("", BitConverter.GetBytes(x).Select(y => y.ToString("x2", CultureInfo.InvariantCulture)));
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
@@ -194,7 +195,7 @@ internal class BlazorMarkdownRenderer(BlazorMarkdownRendererOptions options, Ren
/// <inheritdoc />
public override void VisitLink(Link link)
{
if (link.Destination.StartsWith("#"))
if (link.Destination?.StartsWith('#') == true)
{
builder.OpenComponent<RadzenAnchor>(0);
builder.AddAttribute(0, "href", link.Destination);
@@ -210,7 +211,7 @@ internal class BlazorMarkdownRenderer(BlazorMarkdownRendererOptions options, Ren
{
builder.OpenComponent<RadzenLink>(0);
if (!HtmlSanitizer.IsDangerousUrl(link.Destination))
if (!string.IsNullOrEmpty(link.Destination) && !HtmlSanitizer.IsDangerousUrl(link.Destination))
{
builder.AddAttribute(1, nameof(RadzenLink.Path), link.Destination);
}
@@ -230,7 +231,7 @@ internal class BlazorMarkdownRenderer(BlazorMarkdownRendererOptions options, Ren
{
builder.OpenComponent<RadzenImage>(0);
if (!HtmlSanitizer.IsDangerousUrl(image.Destination))
if (!string.IsNullOrEmpty(image.Destination) && !HtmlSanitizer.IsDangerousUrl(image.Destination))
{
builder.AddAttribute(1, nameof(RadzenImage.Path), image.Destination);
}
@@ -291,7 +292,7 @@ internal class BlazorMarkdownRenderer(BlazorMarkdownRendererOptions options, Ren
if (match.Success)
{
var markerId = Convert.ToInt32(match.Groups[1].Value);
var markerId = int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
outlet(builder, markerId);
}
else if (options.AllowHtml)
@@ -349,11 +350,12 @@ internal class BlazorMarkdownRenderer(BlazorMarkdownRendererOptions options, Ren
/// <inheritdoc />
public override void VisitHtmlInline(HtmlInline htmlInline)
{
if (htmlInline.Value == null) return;
var match = OutletRegex.Match(htmlInline.Value);
if (match.Success)
{
var markerId = Convert.ToInt32(match.Groups[1].Value);
var markerId = Convert.ToInt32(match.Groups[1].Value, CultureInfo.InvariantCulture);
outlet(builder, markerId);
return;
}
@@ -406,7 +408,7 @@ internal class BlazorMarkdownRenderer(BlazorMarkdownRendererOptions options, Ren
}
}
if (html.EndsWith("/>") || IsVoidElement(tagName))
if (html.EndsWith("/>", StringComparison.Ordinal) || IsVoidElement(tagName))
{
builder.CloseElement();
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace Radzen.Blazor.Markdown;
@@ -29,6 +30,7 @@ public abstract class BlockContainer : Block
/// <returns>The added block.</returns>
public virtual T Add<T>(T block) where T : Block
{
ArgumentNullException.ThrowIfNull(block);
children.Add(block);
block.Parent = this;
@@ -43,6 +45,8 @@ public abstract class BlockContainer : Block
/// <param name="target">The block to replace with.</param>
public void Replace(Block source, Block target)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(target);
var index = children.IndexOf(source);
if (index >= 0)

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
@@ -9,6 +11,7 @@ public class BlockQuote : BlockContainer
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitBlockQuote(this);
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -14,6 +16,7 @@ public class Code(string value) : Inline
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitCode(this);
}
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -17,6 +19,7 @@ public class Document : BlockContainer
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitDocument(this);
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,6 +10,7 @@ public class Emphasis : InlineContainer
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitEmphasis(this);
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Text.RegularExpressions;
namespace Radzen.Blazor.Markdown;
@@ -10,17 +11,18 @@ public class FencedCodeBlock : Leaf
/// <summary>
/// The delimiter used to start and end the code block.
/// </summary>
public string Delimiter { get; private set; }
public string? Delimiter { get; private set; }
internal int Indent { get; private set; }
/// <summary>
/// The info string of the code block. This is the first line of the code block and is used to specify the language of the code block.
/// </summary>
public string Info { get; private set; }
public string? Info { get; private set; }
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitFencedCodeBlock(this);
}
@@ -29,7 +31,7 @@ public class FencedCodeBlock : Leaf
base.Close(parser);
// first line becomes info string
var newlinePos = Value.IndexOf('\n');
var newlinePos = Value.IndexOf('\n', StringComparison.Ordinal);
var firstLine = Value[..newlinePos];
Info = firstLine.Trim();
Value = Value[(newlinePos + 1)..];
@@ -43,7 +45,7 @@ public class FencedCodeBlock : Leaf
var match = ClosingFenceRegex.Match(line);
if (indent <= 3 && parser.PeekNonSpace() == Delimiter[0] && match.Success && match.Length >= Delimiter.Length)
if (indent <= 3 && Delimiter != null && parser.PeekNonSpace() == Delimiter[0] && match.Success && match.Length >= Delimiter.Length)
{
// closing fence - we're at end of line, so we can return
parser.LastLineLength = parser.Offset + indent + match.Length;

View File

@@ -1,4 +1,6 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -14,6 +16,7 @@ public abstract class Heading : Leaf
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitHeading(this);
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Text.RegularExpressions;
namespace Radzen.Blazor.Markdown;
@@ -12,6 +13,7 @@ public class HtmlBlock : Leaf
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitHtmlBlock(this);
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,12 +10,13 @@ public class HtmlInline : Inline
/// <summary>
/// Gets or sets the HTML element value.
/// </summary>
public string Value { get; set; }
public string? Value { get; set; }
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitHtmlInline(this);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
@@ -78,7 +79,7 @@ class HtmlSanitizer
var safeAttributes = Regex.Replace(attributes, @"(\w+)\s*=\s*(""[^""]*""|'[^']*'|[^\s>]+)", SanitizeAttribute);
return $"<{(match.Value.StartsWith("</") ? "/" : "")}{tag}{safeAttributes}>";
return $"<{(match.Value.StartsWith("</", StringComparison.Ordinal) ? "/" : "")}{tag}{safeAttributes}>";
}
private string SanitizeAttribute(Match match)
@@ -128,9 +129,9 @@ class HtmlSanitizer
var decoded = HtmlDecode(value).Trim().ToLowerInvariant();
return decoded.StartsWith("javascript:") ||
decoded.StartsWith("vbscript:") ||
decoded.StartsWith("data:text/html") ||
decoded.Contains("expression(");
return decoded.StartsWith("javascript:", StringComparison.Ordinal) ||
decoded.StartsWith("vbscript:", StringComparison.Ordinal) ||
decoded.StartsWith("data:text/html", StringComparison.Ordinal) ||
decoded.Contains("expression(", StringComparison.Ordinal);
}
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,16 +10,17 @@ public class Image : InlineContainer
/// <summary>
/// Gets or sets the destination (URL) of the image.
/// </summary>
public string Destination { get; set; }
public string? Destination { get; set; }
/// <summary>
/// Gets or sets the alternative text of the image.
/// </summary>
public string Title { get; set; }
public string? Title { get; set; }
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitImage(this);
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
@@ -11,6 +12,7 @@ public class IndentedCodeBlock : Leaf
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitIndentedCodeBlock(this);
}

View File

@@ -12,7 +12,7 @@ class InlineParser
public char Char { get; set; }
public int Length { get; set; }
public int Position { get; set; }
public Text Node { get; set; }
public Text? Node { get; set; }
public bool CanOpen { get; set; }
public bool CanClose { get; set; }
public bool Active { get; set; } = true;
@@ -296,7 +296,7 @@ class InlineParser
var url = destination.ToString();
if (url.Contains(Space))
if (url.Contains(Space, StringComparison.Ordinal))
{
return false;
}
@@ -778,7 +778,7 @@ class InlineParser
private void ReplaceOpener(int openerIndex, InlineContainer parent)
{
var startIndex = inlines.FindIndex(delimiters[openerIndex].Node.Equals);
var startIndex = delimiters[openerIndex].Node != null ? inlines.FindIndex(node => node.Equals(delimiters[openerIndex].Node)) : -1;
ParseEmphasisAndStrong(openerIndex);
@@ -815,7 +815,7 @@ class InlineParser
AddTextNode();
var startIndex = inlines.FindIndex(delimiters[openerIndex].Node.Equals);
var startIndex = delimiters[openerIndex].Node != null ? inlines.FindIndex(node => node.Equals(delimiters[openerIndex].Node)) : -1;
var endIndex = inlines.Count - startIndex;
@@ -878,8 +878,8 @@ class InlineParser
{
var closer = delimiters[closerIndex];
var opener = delimiters[openerIndex];
var startIndex = inlines.FindIndex(opener.Node.Equals);
var endIndex = inlines.FindIndex(closer.Node.Equals);
var startIndex = opener.Node != null ? inlines.FindIndex(opener.Node.Equals) : -1;
var endIndex = closer.Node != null ? inlines.FindIndex(closer.Node.Equals) : -1;
if (startIndex >= 0 && endIndex >= 0)
{
@@ -896,7 +896,7 @@ class InlineParser
opener.Length -= charsToConsume;
if (opener.Length > 0)
if (opener.Length > 0 && opener.Node != null)
{
opener.Node.Value = opener.Node.Value[..^charsToConsume];
startIndex += 1;
@@ -904,7 +904,7 @@ class InlineParser
closer.Length -= charsToConsume;
if (closer.Length > 0)
if (closer.Length > 0 && closer.Node != null)
{
closer.Node.Value = closer.Node.Value[..^charsToConsume];
endIndex -= 1;

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,6 +10,7 @@ public class LineBreak : Inline
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitLineBreak(this);
}
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,16 +10,17 @@ public class Link : InlineContainer
/// <summary>
/// Gets or sets the destination (URL) of the link.
/// </summary>
public string Destination { get; set; }
public string? Destination { get; set; }
/// <summary>
/// Gets or sets the link title.
/// </summary>
public string Title { get; set; }
public string? Title { get; set; }
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitLink(this);
}
}

View File

@@ -2,6 +2,6 @@ namespace Radzen.Blazor.Markdown;
class LinkReference
{
public string Destination { get; set; }
public string Title { get; set; }
public string? Destination { get; set; }
public string? Title { get; set; }
}

View File

@@ -17,7 +17,7 @@ public abstract class List : BlockContainer
/// </summary>
public bool Tight { get; set; } = true;
internal int Padding { get; set; }
internal string Delimiter { get; set; }
internal string? Delimiter { get; set; }
internal override BlockMatch Matches(BlockParser parser)
{

View File

@@ -1,3 +1,5 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;
namespace Radzen.Blazor.Markdown;
@@ -12,6 +14,7 @@ public class ListItem : BlockContainer
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitListItem(this);
}
@@ -126,7 +129,7 @@ public class ListItem : BlockContainer
var list = new OrderedList
{
MarkerOffset = parser.Indent,
Start = int.Parse(match.Groups[1].Value),
Start = int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture),
Delimiter = match.Groups[2].Value
};
data = list;

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
namespace Radzen.Blazor.Markdown;
@@ -11,32 +12,56 @@ public abstract class NodeVisitorBase : INodeVisitor
/// <summary>
/// Visits a block quote by visiting its children.
/// </summary>
public virtual void VisitBlockQuote(BlockQuote blockQuote) => VisitChildren(blockQuote.Children);
public virtual void VisitBlockQuote(BlockQuote blockQuote)
{
ArgumentNullException.ThrowIfNull(blockQuote);
VisitChildren(blockQuote.Children);
}
/// <summary>
/// Visits a document by visiting its children.
/// </summary>
public virtual void VisitDocument(Document document) => VisitChildren(document.Children);
public virtual void VisitDocument(Document document)
{
ArgumentNullException.ThrowIfNull(document);
VisitChildren(document.Children);
}
/// <summary>
/// Visits a heading by visiting its children.
/// </summary>
public virtual void VisitHeading(Heading heading) => VisitChildren(heading.Children);
public virtual void VisitHeading(Heading heading)
{
ArgumentNullException.ThrowIfNull(heading);
VisitChildren(heading.Children);
}
/// <summary>
/// Visits a list item by visiting its children.
/// </summary>
public virtual void VisitListItem(ListItem listItem) => VisitChildren(listItem.Children);
public virtual void VisitListItem(ListItem listItem)
{
ArgumentNullException.ThrowIfNull(listItem);
VisitChildren(listItem.Children);
}
/// <summary>
/// Visits an ordered list by visiting its children.
/// </summary>
public virtual void VisitOrderedList(OrderedList orderedList) => VisitChildren(orderedList.Children);
public virtual void VisitOrderedList(OrderedList orderedList)
{
ArgumentNullException.ThrowIfNull(orderedList);
VisitChildren(orderedList.Children);
}
/// <summary>
/// Visits a paragraph by visiting its children.
/// </summary>
public virtual void VisitParagraph(Paragraph paragraph) => VisitChildren(paragraph.Children);
public virtual void VisitParagraph(Paragraph paragraph)
{
ArgumentNullException.ThrowIfNull(paragraph);
VisitChildren(paragraph.Children);
}
/// <summary>
/// Visits a thematic break.
@@ -83,27 +108,47 @@ public abstract class NodeVisitorBase : INodeVisitor
/// <summary>
/// Visits an ordered list by visiting its children.
/// </summary>
public virtual void VisitUnorderedList(UnorderedList unorderedList) => VisitChildren(unorderedList.Children);
public virtual void VisitUnorderedList(UnorderedList unorderedList)
{
ArgumentNullException.ThrowIfNull(unorderedList);
VisitChildren(unorderedList.Children);
}
/// <summary>
/// Visits an emphasis by visiting its children.
/// </summary>
public virtual void VisitEmphasis(Emphasis emphasis) => VisitChildren(emphasis.Children);
public virtual void VisitEmphasis(Emphasis emphasis)
{
ArgumentNullException.ThrowIfNull(emphasis);
VisitChildren(emphasis.Children);
}
/// <summary>
/// Visits a strong by visiting its children.
/// </summary>
public virtual void VisitStrong(Strong strong) => VisitChildren(strong.Children);
public virtual void VisitStrong(Strong strong)
{
ArgumentNullException.ThrowIfNull(strong);
VisitChildren(strong.Children);
}
/// <summary>
/// Visits a link by visiting its children.
/// </summary>
public virtual void VisitLink(Link link) => VisitChildren(link.Children);
public virtual void VisitLink(Link link)
{
ArgumentNullException.ThrowIfNull(link);
VisitChildren(link.Children);
}
/// <summary>
/// Visits an image by visiting its children.
/// </summary>
public virtual void VisitImage(Image image) => VisitChildren(image.Children);
public virtual void VisitImage(Image image)
{
ArgumentNullException.ThrowIfNull(image);
VisitChildren(image.Children);
}
/// <summary>
/// Visits a code block.
@@ -129,28 +174,45 @@ public abstract class NodeVisitorBase : INodeVisitor
/// <summary>
/// Visits a table.
/// </summary>
public virtual void VisitTable(Table table) => VisitChildren(table.Rows);
public virtual void VisitTable(Table table)
{
ArgumentNullException.ThrowIfNull(table);
VisitChildren(table.Rows);
}
/// <summary>
/// Visits a table header row by visiting its children.
/// </summary>
public virtual void VisitTableHeaderRow(TableHeaderRow header) => VisitChildren(header.Cells);
public virtual void VisitTableHeaderRow(TableHeaderRow header)
{
ArgumentNullException.ThrowIfNull(header);
VisitChildren(header.Cells);
}
/// <summary>
/// Visits a table row by visiting its children.
/// </summary>
public virtual void VisitTableRow(TableRow row) => VisitChildren(row.Cells);
public virtual void VisitTableRow(TableRow row)
{
ArgumentNullException.ThrowIfNull(row);
VisitChildren(row.Cells);
}
/// <summary>
/// Visits a table cell by visiting its children.
/// </summary>
public virtual void VisitTableCell(TableCell cell) => VisitChildren(cell.Children);
public virtual void VisitTableCell(TableCell cell)
{
ArgumentNullException.ThrowIfNull(cell);
VisitChildren(cell.Children);
}
/// <summary>
/// Visits a collection of nodes.
/// </summary>
protected void VisitChildren(IEnumerable<INode> children)
{
ArgumentNullException.ThrowIfNull(children);
foreach (var node in children)
{
node.Accept(this);

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,6 +10,7 @@ public class OrderedList : List
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitOrderedList(this);
}

View File

@@ -1,4 +1,6 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -9,6 +11,7 @@ public class Paragraph : Leaf
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitParagraph(this);
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,6 +10,7 @@ public class SoftLineBreak : Inline
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitSoftLineBreak(this);
}
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,6 +10,7 @@ public class Strong : InlineContainer
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitStrong(this);
}
}

View File

@@ -19,6 +19,7 @@ public class Table : Leaf
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitTable(this);
}
@@ -137,7 +138,7 @@ public class Table : Leaf
// Check if the line contains a pipe character to be more specific about table delimiters
// This helps avoid misinterpreting heading delimiters as table delimiters
if (!line.Contains('|') && !line.Contains(':'))
if (!line.Contains('|', StringComparison.Ordinal) && !line.Contains(':', StringComparison.Ordinal))
{
return BlockStart.Skip;
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
namespace Radzen.Blazor.Markdown;
@@ -47,6 +48,7 @@ public class TableCell : INode, IBlockInlineContainer
/// <inheritdoc />
public void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitTableCell(this);
}
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,6 +10,7 @@ public class TableHeaderRow : TableRow
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitTableHeaderRow(this);
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
namespace Radzen.Blazor.Markdown;
@@ -28,6 +29,7 @@ public class TableRow : INode
/// <inheritdoc />
public virtual void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitTableRow(this);
}
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -13,6 +15,7 @@ public class Text(string value) : Inline
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitText(this);
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Text.RegularExpressions;
namespace Radzen.Blazor.Markdown;
@@ -12,6 +13,7 @@ public class ThematicBreak : Block
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitThematicBreak(this);
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Radzen.Blazor.Markdown;
/// <summary>
@@ -8,6 +10,7 @@ public class UnorderedList : List
/// <inheritdoc />
public override void Accept(INodeVisitor visitor)
{
ArgumentNullException.ThrowIfNull(visitor);
visitor.VisitUnorderedList(this);
}
}

View File

@@ -10,16 +10,16 @@ public class MenuItemEventArgs : MouseEventArgs
/// <summary>
/// Gets text of the clicked item.
/// </summary>
public string Text { get; internal set; }
public string? Text { get; internal set; }
/// <summary>
/// Gets the value of the clicked item.
/// </summary>
public object Value { get; internal set; }
public object? Value { get; internal set; }
/// <summary>
/// Gets the path path of the clicked item.
/// </summary>
public string Path { get; internal set; }
public string? Path { get; internal set; }
}

Some files were not shown because too many files have changed in this diff Show More