Compare commits

..

4 Commits

Author SHA1 Message Date
Vladimir Enchev
14e39f665f Version updated 2026-02-11 11:36:55 +02:00
Vladimir Enchev
2f77a0b849 Fixed Invalid operation exception with complex filter queries
Expression.Default support added to ExpressionSerializer
2026-02-11 11:36:35 +02:00
Vladimir Enchev
c3af020b81 Tooltip position in RTL mode improved 2026-02-11 11:16:53 +02:00
Vladimir Enchev
19460b899b Fix to possible DotNetObjectReference instance was already disposed 2026-02-11 10:50:41 +02:00
5 changed files with 138 additions and 64 deletions

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -319,5 +319,35 @@ namespace Radzen.Blazor.Tests
Expression<Func<TestEntity, bool>> expr = e => !e.Tags.Contains("Member");
Assert.Equal("e => (!(e.Tags.Contains(\"Member\")))", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DefaultExpression_ReferenceType()
{
// Simulates the NullPropagate pattern: x.Address == null ? default(string) : x.Address.City
var param = Expression.Parameter(typeof(TestEntity), "x");
var address = Expression.Property(param, "Address");
var city = Expression.Property(address, "City");
var isNull = Expression.Equal(address, Expression.Constant(null, typeof(Address)));
var whenNull = Expression.Default(typeof(string));
var conditional = Expression.Condition(isNull, whenNull, city);
var lambda = Expression.Lambda<Func<TestEntity, string>>(conditional, param);
Assert.Equal("x => ((x.Address == null) ? null : x.Address.City)", _serializer.Serialize(lambda));
}
[Fact]
public void Serializes_DefaultExpression_ValueType()
{
// Simulates a conditional with a default value type
var param = Expression.Parameter(typeof(TestEntity), "x");
var address = Expression.Property(param, "Address");
var age = Expression.Property(param, "Age");
var isNull = Expression.Equal(address, Expression.Constant(null, typeof(Address)));
var whenNull = Expression.Default(typeof(int));
var conditional = Expression.Condition(isNull, whenNull, age);
var lambda = Expression.Lambda<Func<TestEntity, int>>(conditional, param);
Assert.Equal("x => ((x.Address == null) ? default(int) : x.Age)", _serializer.Serialize(lambda));
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
@@ -272,6 +272,21 @@ public class ExpressionSerializer : ExpressionVisitor
return node;
}
/// <inheritdoc/>
protected override Expression VisitDefault(DefaultExpression node)
{
ArgumentNullException.ThrowIfNull(node);
if (!node.Type.IsValueType || Nullable.GetUnderlyingType(node.Type) != null)
{
_sb.Append("null");
}
else
{
_sb.Append(CultureInfo.InvariantCulture, $"default({node.Type.DisplayName(true).Replace("+", ".", StringComparison.Ordinal)})");
}
return node;
}
/// <summary>
/// Maps an ExpressionType to its corresponding C# operator.
/// </summary>

View File

@@ -11,7 +11,7 @@
<IsPackable>true</IsPackable>
<PackageId>Radzen.Blazor</PackageId>
<Product>Radzen.Blazor</Product>
<Version>9.0.2</Version>
<Version>9.0.3</Version>
<Copyright>Radzen Ltd.</Copyright>
<Authors>Radzen Ltd.</Authors>
<Description>Radzen Blazor is the most sophisticated free UI component library for Blazor, featuring 100+ native components including DataGrid, Scheduler, Charts, and advanced theming with full support for Material Design and Fluent UI.</Description>

View File

@@ -326,8 +326,6 @@ namespace Radzen
debouncer?.Dispose();
debouncer = null;
reference?.Dispose();
reference = null;
if (IsJSRuntimeAvailable && JSRuntime != null && !string.IsNullOrEmpty(UniqueID))
{
@@ -346,6 +344,9 @@ namespace Radzen
JSRuntime.InvokeVoid("Radzen.removeMouseLeave", UniqueID);
}
}
reference?.Dispose();
reference = null;
}
/// <summary>

View File

@@ -92,6 +92,7 @@ window.Radzen = {
var handler = function (e) {
e.stopPropagation();
e.preventDefault();
try {
ref.invokeMethodAsync('RadzenComponent.RaiseContextMenu',
{
ClientX: e.clientX,
@@ -105,6 +106,7 @@ window.Radzen = {
Button: e.button,
Buttons: e.buttons,
});
} catch { }
return false;
};
Radzen[id + 'contextmenu'] = handler;
@@ -115,7 +117,7 @@ window.Radzen = {
var el = document.getElementById(id);
if (el) {
var handler = function (e) {
ref.invokeMethodAsync('RadzenComponent.RaiseMouseEnter');
try { ref.invokeMethodAsync('RadzenComponent.RaiseMouseEnter'); } catch { }
};
Radzen[id + 'mouseenter'] = handler;
el.addEventListener('mouseenter', handler, false);
@@ -125,7 +127,7 @@ window.Radzen = {
var el = document.getElementById(id);
if (el) {
var handler = function (e) {
ref.invokeMethodAsync('RadzenComponent.RaiseMouseLeave');;
try { ref.invokeMethodAsync('RadzenComponent.RaiseMouseLeave'); } catch { }
};
Radzen[id + 'mouseleave'] = handler;
el.addEventListener('mouseleave', handler, false);
@@ -254,9 +256,9 @@ window.Radzen = {
});
Radzen[id].instance.addListener('click', function (e) {
Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMapClick', {
try { Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMapClick', {
Position: {Lat: e.latLng.lat(), Lng: e.latLng.lng()}
});
}); } catch { }
});
Radzen.updateMap(id, apiKey, zoom, center, markers, options, fitBoundsToMarkersOnUpdate, language);
@@ -302,11 +304,11 @@ window.Radzen = {
});
marker.addListener('click', function (e) {
Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMarkerClick', {
try { Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMarkerClick', {
Title: marker.title,
Label: marker.content.innerText,
Position: marker.position
});
}); } catch { }
});
marker.setMap(Radzen[id].instance);
@@ -392,7 +394,7 @@ window.Radzen = {
var code = Radzen[id].inputs.map(i => i.value).join('').trim();
hidden.value = code;
ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', code);
try { ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', code); } catch { }
Radzen[id].inputs[Radzen[id].inputs.length - 1].focus();
}
@@ -432,7 +434,7 @@ window.Radzen = {
var value = Radzen[id].inputs.map(i => i.value).join('').trim();
hidden.value = value;
ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value);
try { ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value); } catch { }
var index = Radzen[id].inputs.indexOf(e.currentTarget);
if (index < Radzen[id].inputs.length - 1) {
@@ -448,7 +450,7 @@ window.Radzen = {
var value = Radzen[id].inputs.map(i => i.value).join('').trim();
hidden.value = value;
ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value);
try { ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value); } catch { }
var index = Radzen[id].inputs.indexOf(e.currentTarget);
if (index > 0) {
@@ -510,11 +512,11 @@ window.Radzen = {
newValue >= min &&
newValue <= max
) {
slider.invokeMethodAsync(
try { slider.invokeMethodAsync(
'RadzenSlider.OnValueChange',
newValue,
!!slider.isMin
);
); } catch { }
}
};
@@ -546,11 +548,11 @@ window.Radzen = {
var newValue = percent * (max - min) + min;
var oldValue = range ? value[slider.isMin ? 0 : 1] : value;
if (newValue >= min && newValue <= max && newValue != oldValue) {
slider.invokeMethodAsync(
try { slider.invokeMethodAsync(
'RadzenSlider.OnValueChange',
newValue,
!!slider.isMin
);
); } catch { }
}
}
};
@@ -840,7 +842,7 @@ window.Radzen = {
uploadComponent.files = Array.from(fileInput.files);
uploadComponent.localFiles = files;
uploadComponent.invokeMethodAsync('RadzenUpload.OnChange', files);
try { uploadComponent.invokeMethodAsync('RadzenUpload.OnChange', files); } catch { }
}
for (var i = 0; i < fileInput.files.length; i++) {
@@ -913,7 +915,7 @@ window.Radzen = {
Radzen.uploadComponents && Radzen.uploadComponents[fileInput.id];
if (uploadComponent) {
var progress = parseInt((e.loaded / e.total) * 100);
uploadComponent.invokeMethodAsync(
try { uploadComponent.invokeMethodAsync(
'RadzenUpload.OnProgress',
progress,
e.loaded,
@@ -925,7 +927,7 @@ window.Radzen = {
cancelled = true;
xhr.abort();
}
});
}); } catch { }
}
}
};
@@ -936,27 +938,27 @@ window.Radzen = {
Radzen.uploadComponents && Radzen.uploadComponents[fileInput.id];
if (uploadComponent) {
if (status === 0 || (status >= 200 && status < 400)) {
uploadComponent.invokeMethodAsync(
try { uploadComponent.invokeMethodAsync(
'RadzenUpload.OnComplete',
xhr.responseText,
cancelled
);
); } catch { }
} else {
uploadComponent.invokeMethodAsync(
try { uploadComponent.invokeMethodAsync(
'RadzenUpload.OnError',
xhr.responseText
);
); } catch { }
}
}
}
};
uploadComponent.invokeMethodAsync('GetHeaders').then(function (headers) {
try { uploadComponent.invokeMethodAsync('GetHeaders').then(function (headers) {
xhr.open(method, url, true);
for (var name in headers) {
xhr.setRequestHeader(name, headers[name]);
}
xhr.send(data);
});
}); } catch { }
},
getCookie: function (name) {
var value = '; ' + decodeURIComponent(document.cookie);
@@ -1236,6 +1238,12 @@ window.Radzen = {
rect.width = x ? rect.width + 20 : rect.width;
rect.height = y ? rect.height + 20 : rect.height;
var isRTL = Radzen.isRTL(popup);
if (isRTL && (position == 'bottom' || position == 'top')) {
left = parentRect.right - rect.width;
}
var smartPosition = !position || position == 'bottom';
if (smartPosition && top + rect.height > window.innerHeight && parentRect.top > rect.height) {
@@ -1276,6 +1284,24 @@ window.Radzen = {
}
}
if (smartPosition && isRTL && left < 0 && window.innerWidth > rect.width) {
left = !position ? 0 : rect.left;
if (position) {
top = y || parentRect.top;
var tooltipContent = popup.children[0];
var tooltipContentClassName = 'rz-' + position + '-tooltip-content';
if (tooltipContent.classList.contains(tooltipContentClassName)) {
tooltipContent.classList.remove(tooltipContentClassName);
tooltipContent.classList.add('rz-right-tooltip-content');
position = 'right';
if (instance && callback) {
try { instance.invokeMethodAsync(callback, position); } catch { }
}
}
}
}
if (smartPosition) {
if (position) {
top = top + 20;
@@ -1294,7 +1320,7 @@ window.Radzen = {
if (position == 'top') {
top = parentRect.top - rect.height + 5;
left = parentRect.left;
left = isRTL ? parentRect.right - rect.width : parentRect.left;
}
popup.style.zIndex = 2000;
@@ -1643,11 +1669,13 @@ window.Radzen = {
dialog.offsetWidth = e[0].target.offsetWidth;
dialog.offsetHeight = e[0].target.offsetHeight;
try {
dialog.invokeMethodAsync(
'RadzenDialog.OnResize',
e[0].target.offsetWidth,
e[0].target.offsetHeight
);
} catch { }
}
};
Radzen.dialogResizer = new ResizeObserver(dialogResize).observe(lastDialog.parentElement);
@@ -1668,7 +1696,7 @@ window.Radzen = {
lastDialog.parentElement.style.left = left + 'px';
lastDialog.parentElement.style.top = top + 'px';
dialog.invokeMethodAsync('RadzenDialog.OnDrag', top, left);
try { dialog.invokeMethodAsync('RadzenDialog.OnDrag', top, left); } catch { }
};
var stop = function () {
@@ -1775,7 +1803,7 @@ window.Radzen = {
var lastDialog = dialogs[dialogs.length - 1];
if (lastDialog && lastDialog.options && lastDialog.options.closeDialogOnEsc) {
Radzen.dialogService.invokeMethodAsync('DialogService.Close', null);
try { Radzen.dialogService.invokeMethodAsync('DialogService.Close', null); } catch { }
if (dialogs.length <= 1) {
document.removeEventListener('keydown', Radzen.closePopupOrDialog);
@@ -1962,7 +1990,7 @@ window.Radzen = {
ref.resizeHandler = function () {
var rect = ref.getBoundingClientRect();
instance.invokeMethodAsync('Resize', rect.width, rect.height);
try { instance.invokeMethodAsync('Resize', rect.width, rect.height); } catch { }
};
if (window.ResizeObserver) {
@@ -1983,7 +2011,7 @@ window.Radzen = {
var rect = ref.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
instance.invokeMethodAsync('MouseMove', x, y);
try { instance.invokeMethodAsync('MouseMove', x, y); } catch { }
}
}, 100);
ref.mouseEnterHandler = function () {
@@ -1994,14 +2022,14 @@ window.Radzen = {
return;
}
inside = false;
instance.invokeMethodAsync('MouseMove', -1, -1);
try { instance.invokeMethodAsync('MouseMove', -1, -1); } catch { }
};
ref.clickHandler = function (e) {
var rect = ref.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
if (!e.target.closest('.rz-marker')) {
instance.invokeMethodAsync('Click', x, y);
try { instance.invokeMethodAsync('Click', x, y); } catch { }
}
};
@@ -2025,7 +2053,7 @@ window.Radzen = {
ref.resizeHandler = function () {
var rect = ref.getBoundingClientRect();
instance.invokeMethodAsync('Resize', rect.width, rect.height);
try { instance.invokeMethodAsync('Resize', rect.width, rect.height); } catch { }
};
window.addEventListener('resize', ref.resizeHandler);
@@ -2074,7 +2102,7 @@ window.Radzen = {
mediaQuery: function(query, instance) {
if (instance) {
function callback(event) {
instance.invokeMethodAsync('OnChange', event.matches)
try { instance.invokeMethodAsync('OnChange', event.matches); } catch { }
};
var query = matchMedia(query);
this.mediaQueries[instance._id] = function() {
@@ -2092,7 +2120,7 @@ window.Radzen = {
},
createEditor: function (ref, uploadUrl, paste, instance, shortcuts) {
ref.inputListener = function () {
instance.invokeMethodAsync('OnChange', ref.innerHTML);
try { instance.invokeMethodAsync('OnChange', ref.innerHTML); } catch { }
};
ref.keydownListener = function (e) {
var key = '';
@@ -2109,7 +2137,7 @@ window.Radzen = {
if (shortcuts.includes(key)) {
e.preventDefault();
instance.invokeMethodAsync('ExecuteShortcutAsync', key);
try { instance.invokeMethodAsync('ExecuteShortcutAsync', key); } catch { }
}
};
@@ -2135,7 +2163,7 @@ window.Radzen = {
ref.selectionChangeListener = function () {
if (document.activeElement == ref) {
instance.invokeMethodAsync('OnSelectionChange');
try { instance.invokeMethodAsync('OnSelectionChange'); } catch { }
}
};
ref.handleInsert = function (e, transfer, hasDelegate) {
@@ -2163,36 +2191,36 @@ window.Radzen = {
var result = JSON.parse(xhr.responseText);
var html = '<img src="' + result.url + '">';
if (hasDelegate) {
instance.invokeMethodAsync('OnPaste', html)
try { instance.invokeMethodAsync('OnPaste', html)
.then(function (html) {
document.execCommand("insertHTML", false, html);
});
}); } catch { }
} else {
document.execCommand("insertHTML", false, '<img src="' + result.url + '">');
}
instance.invokeMethodAsync('OnUploadComplete', xhr.responseText);
try { instance.invokeMethodAsync('OnUploadComplete', xhr.responseText); } catch { }
} else {
instance.invokeMethodAsync('OnError', xhr.responseText);
try { instance.invokeMethodAsync('OnError', xhr.responseText); } catch { }
}
}
}
instance.invokeMethodAsync('GetHeaders').then(function (headers) {
try { instance.invokeMethodAsync('GetHeaders').then(function (headers) {
xhr.open('POST', uploadUrl, true);
for (var name in headers) {
xhr.setRequestHeader(name, headers[name]);
}
xhr.send(data);
});
}); } catch { }
} else {
var reader = new FileReader();
reader.onload = function (e) {
var html = '<img src="' + e.target.result + '">';
if (hasDelegate) {
instance.invokeMethodAsync('OnPaste', html)
try { instance.invokeMethodAsync('OnPaste', html)
.then(function (html) {
document.execCommand("insertHTML", false, html);
});
}); } catch { }
} else {
document.execCommand("insertHTML", false, html);
}
@@ -2217,11 +2245,11 @@ window.Radzen = {
data = data.substring(startIndex + startMarker.length, endIndex).trim();
}
instance.invokeMethodAsync('OnPaste', data)
try { instance.invokeMethodAsync('OnPaste', data)
.then(ref.focus())
.then(function (html) {
document.execCommand("insertHTML", false, html);
});
}); } catch { }
}
ref.pasteListener = function (e) {
ref.handleInsert(e, e.clipboardData, paste);
@@ -2318,11 +2346,11 @@ window.Radzen = {
return { left: 0, top: 0, width: 0, height: 0 };
}
ref.mouseMoveHandler = function (e) {
instance.invokeMethodAsync(handler, { clientX: e.clientX, clientY: e.clientY });
try { instance.invokeMethodAsync(handler, { clientX: e.clientX, clientY: e.clientY }); } catch { }
};
ref.touchMoveHandler = function (e) {
if (e.targetTouches[0] && ref.contains(e.targetTouches[0].target)) {
instance.invokeMethodAsync(handler, { clientX: e.targetTouches[0].clientX, clientY: e.targetTouches[0].clientY });
try { instance.invokeMethodAsync(handler, { clientX: e.targetTouches[0].clientX, clientY: e.targetTouches[0].clientY }); } catch { }
}
};
ref.mouseUpHandler = function (e) {
@@ -2428,11 +2456,11 @@ window.Radzen = {
var cell = el.parentNode.parentNode;
if (!cell) return;
if (Radzen[el]) {
grid.invokeMethodAsync(
try { grid.invokeMethodAsync(
'RadzenGrid.OnColumnResized',
columnIndex,
cell.getBoundingClientRect().width
);
); } catch { }
el.style.width = null;
document.removeEventListener('mousemove', Radzen[el].mouseMoveHandler);
document.removeEventListener('mouseup', Radzen[el].mouseUpHandler);
@@ -2452,11 +2480,11 @@ window.Radzen = {
width: cell.getBoundingClientRect().width,
mouseUpHandler: function (e) {
if (Radzen[el]) {
grid.invokeMethodAsync(
try { grid.invokeMethodAsync(
'RadzenGrid.OnColumnResized',
columnIndex,
cell.getBoundingClientRect().width
);
); } catch { }
el.style.width = null;
document.removeEventListener('mousemove', Radzen[el].mouseMoveHandler);
document.removeEventListener('mouseup', Radzen[el].mouseUpHandler);
@@ -2578,13 +2606,13 @@ window.Radzen = {
paneNextLength: isFinite(paneNextLength) ? paneNextLength : 0,
mouseUpHandler: function(e) {
if (Radzen[el]) {
splitter.invokeMethodAsync(
try { splitter.invokeMethodAsync(
'RadzenSplitter.OnPaneResized',
parseInt(pane.getAttribute('data-index')),
parseFloat(pane.style.flexBasis),
paneNext ? parseInt(paneNext.getAttribute('data-index')) : null,
paneNext ? parseFloat(paneNext.style.flexBasis) : null
);
); } catch { }
document.removeEventListener('pointerup', Radzen[el].mouseUpHandler);
document.removeEventListener('pointermove', Radzen[el].mouseMoveHandler);
@@ -2595,9 +2623,9 @@ window.Radzen = {
mouseMoveHandler: function(e) {
if (Radzen[el]) {
splitter.invokeMethodAsync(
try { splitter.invokeMethodAsync(
'RadzenSplitter.OnPaneResizing'
);
); } catch { }
var spacePerc = Radzen[el].panePerc + Radzen[el].paneNextPerc;
var spaceLength = Radzen[el].paneLength + Radzen[el].paneNextLength;
@@ -2712,10 +2740,10 @@ window.Radzen = {
let current = event.results[event.results.length - 1][0]
let result = current.transcript;
componentRef.invokeMethodAsync("OnResult", result);
try { componentRef.invokeMethodAsync("OnResult", result); } catch { }
};
radzenRecognition.onend = function (event) {
componentRef.invokeMethodAsync("StopRecording");
try { componentRef.invokeMethodAsync("StopRecording"); } catch { }
radzenRecognition = null;
};
radzenRecognition.start();
@@ -2825,7 +2853,7 @@ window.Radzen = {
currentSelector = match;
if (!this.selectedNavigationSelector || (match === this.selectedNavigationSelector)) {
this.navigateTo(currentSelector, false);
ref.invokeMethodAsync('ScrollIntoView', currentSelector);
try { ref.invokeMethodAsync('ScrollIntoView', currentSelector); } catch { }
}
}
// clear selected navigation selector after scroll completes
@@ -2867,7 +2895,7 @@ window.Radzen = {
createDraggable: function(element, ref, onDragStart) {
function handleDragStart(e) {
e.dataTransfer.setData('', e.target.id);
ref.invokeMethodAsync(onDragStart);
try { ref.invokeMethodAsync(onDragStart); } catch { }
}
element.draggable = true;
element.addEventListener('dragstart', handleDragStart);
@@ -2887,7 +2915,7 @@ Radzen.registerFabMenu = function(element, dotnet){
}
const handler = function(e){
if(!element.contains(e.target)){
dotnet.invokeMethodAsync('CloseAsync');
try { dotnet.invokeMethodAsync('CloseAsync'); } catch { }
}
};
element.__rzOutsideClickHandler = handler;