I'm trying to figure out how to autofit a column in a nested grid.
In the Detail Template of a grid (Grid A), there is a another grid (Grid B). This nested grid (Grid B) has a column that shows the selections from a MultiSelect component. This could be a single value or multiple values. I know that there are methods that resize a column based on the column id or resize multiple columns, but these methods require a grid object reference. Since this nested grid (Grid B) is templated and therefore has multiple instances, I can't directly get a reference to it.
Also, it seems that all the relevant Grid event handlers only have arguments return references to the data bound to the grid, but not a reference to the grid object.
Is there a way to get a reference to a specific grid instance in a Detail Template or is there another way to auto size a specific column in a nested grid?
Thanks
Hi, my code use OnStateInit to change the column order, settings the GridColumnState.Index property.
Now i found it not work anymore (i can't found the cause).
This is a simplified version who reply the issue: grid is created with Col1 and Col2, in OnStateInit i revert the index of the columns so i assume who grid will render with Col2 and Col1 (inverted order) but seem not working.
https://blazorrepl.telerik.com/QeFwbnFd19xPAId547
What's wrong?
Thanks
I am working with a DataGrid, and in the on-save event,
I need to validate some data. In certain cases,
I must display a dialog that allows the user to decide whether to save or cancel the update,
based on data retrieved from the API.
Is it possible to "hack" the DialogFactory to achieve this?
Hi, I need the SchedulerResourceGroupHeaderTemplate to have a certain size to fit a long description of the appointment and some buttons for actions. I am using the TelerikScheduler component to represent when a person should take one or several medications, but when I change the time division from 12h to 8h or lower the position of the event/appointment gets completely out of position.
Why does it position itself in this way? Am I using the component incorrectly?
Im using telerikSchedulerRef to refresh the UI, when time division changes
telerikSchedulerRef?.Rebind();
and OnItemRenderHandler to add a css class for resizing the appointment control (.k-event)
private void OnItemRenderHandler(SchedulerItemRenderEventArgs e) => e.Class += " customAppointment";
Thanks in advance
<TelerikScheduler Data="@ViewModel.Appointments" @bind-Date="@filterStartDateTime" View="SchedulerView.Timeline" @ref="telerikSchedulerRef" OnItemRender="@OnItemRenderHandler">
<SchedulerToolBar>
<SchedulerToolBarCustomTool>
<div class="mr-2">
<TelerikButton OnClick="()=>SetTimeDivisionMode(TimeDivision.Day)" Size="sm">24h</TelerikButton>
<TelerikButton OnClick="()=>SetTimeDivisionMode(TimeDivision.HalfDay)" Size="sm">12h</TelerikButton>
<TelerikButton OnClick="()=>SetTimeDivisionMode(TimeDivision.EightHours)" Size="sm">8h</TelerikButton>
<TelerikButton OnClick="()=>SetTimeDivisionMode(TimeDivision.Hourly)" Size="sm">1h</TelerikButton>
<TelerikButton OnClick="()=>SetTimeDivisionMode(TimeDivision.HalfHour)" Size="sm">30m</TelerikButton>
<TelerikButton OnClick="()=>SetTimeDivisionMode(TimeDivision.TenMinutes)" Size="sm">10m</TelerikButton>
<TelerikButton OnClick="()=>SetTimeDivisionMode(TimeDivision.FiveMinutes)" Size="sm" Class="mr-3">5m</TelerikButton>
</div>
</SchedulerToolBarCustomTool>
<SchedulerToolBarCustomTool>
<div class="time-filters">
<TelerikDateTimePicker Title="@Localizer["AppStrings.StartDate"]" @bind-Value="@filterStartDateTime" Format="dd/MM/yyyy HH:mm" Width="160px"></TelerikDateTimePicker>
<TelerikFontIcon Icon="@FontIcon.ArrowRight" Size="md" />
<TelerikDateTimePicker Title="@Localizer["AppStrings.Enddate"]" @bind-Value="@filterEndDateTime" Format="dd/MM/yyyy HH:mm" Width="160px"></TelerikDateTimePicker>
</div>
</SchedulerToolBarCustomTool>
</SchedulerToolBar>
<SchedulerResources>
<SchedulerResource Field="@nameof(Appointment.LineId)" TextField="Text" ValueField="Value" Data="@ViewModel.Resources"></SchedulerResource>
</SchedulerResources>
<SchedulerSettings>
<SchedulerGroupSettings Resources="@GroupingResources" Orientation="@SchedulerGroupOrientation.Vertical"></SchedulerGroupSettings>
</SchedulerSettings>
<SchedulerViews>
<SchedulerTimelineView StartTime="@filterStartDateTime" EndTime="@filterEndDateTime" SlotDivisions="@slotDivisions" SlotDuration="@slotDuration">
<SchedulerResourceGroupHeaderTemplate>
<div class="appointment_container" style="min-height:130px;">
<div class="appoint-left">
<TelerikButton Icon="@FontIcon.FileAdd" Size="sm" />
</div>
<div class="appoint-center">
<div class="appointment_desc" title="@context.Text">
@context.Text
</div>
<div class="appointment_size">
Total: @context.Size ml/24h
</div>
</div>
<div class="appoint-right">
<TelerikButton Icon="@FontIcon.Envelop" Size="sm" Class="appoint-envelop" />
<span style="font-style:italic">
@context.Route
</span>
</div>
</div>
</SchedulerResourceGroupHeaderTemplate>
<ItemTemplate>
@{
<div class="d-flex justify-content-between">
<TelerikDropDownButton Icon="@FontIcon.Checkbox">
<DropDownButtonItems>
<DropDownButtonItem Icon="@FontIcon.Check"> Check</DropDownButtonItem>
<DropDownButtonItem Icon="@FontIcon.Pause">Pause</DropDownButtonItem>
<DropDownButtonItem Icon="@FontIcon.MinusCircle">Stop</DropDownButtonItem>
<DropDownButtonItem Icon="@FontIcon.Cart">Add to cart</DropDownButtonItem>
</DropDownButtonItems>
</TelerikDropDownButton>
<span>Test description</span>
</div>
}
</ItemTemplate>
</SchedulerTimelineView>
</SchedulerViews>
</TelerikScheduler>
I was following this example here.
<GridColumn Field=@nameof(BookingEquipmentModel.FirstRequestedEquipmentType) Title="1st Requested" Editable="true" Filterable="false" Width="7rem">
<EditorTemplate>
@{
_currentBookingEquipmentModel = context as BookingEquipmentModel;
<TelerikDropDownList Data="@_equipmentTypeList"
@bind-Value="@_currentBookingEquipmentModel.FirstRequestedEquipmentTypeId"
TextField="@nameof(EquipmentTypeModel.Code)"
ValueField="@nameof(EquipmentTypeModel.EquipmentTypeId)"
DefaultText="Select Type">
<DropDownListSettings>
<DropDownListPopupSettings Height="auto" />
</DropDownListSettings>
</TelerikDropDownList>
}
</EditorTemplate>
</GridColumn>
Grid EditMode set to GridEditMode.Inline and Column is Editable = True. _equipmentTypeList is populated in OnAfterRenderAsync. It's behaving as if it's just a normal read only column?
Rob.
<FormItem Field="@nameof(User.Password)">
<Template>
<TelerikTextBox @bind-Value="@user.Password"
Size="@ThemeConstants.TextBox.Size.Large"
Placeholder="Contraseña"
ShowSuffixSeparator="false"
Password="@IsPasswordHidden">
<TextBoxSuffixTemplate>
<TelerikSvgIcon
Icon="@SvgIcon.Eye"
Size="@ThemeConstants.SvgIcon.Size.Medium"
onmousedown="@(() => MostrarPassword())"
onmouseup="@(() => OcultarPassword())"
onmouseleave="@(() => OcultarPassword())">
</TelerikSvgIcon>
</TextBoxSuffixTemplate>
</TelerikTextBox>
</Template>
</FormItem>
I am trying to use Telerik Blazor Scheduler to display a Google Calendar. Need to be able to add, edit, delete events from Scheduler. Need to be able to manage recurrence events. I am new to Telerik Scheduler and running into a few issues that might be easily spotted to someone else.
The code will connect to the Google Calendar API and access the data. There is issues with recurrence event as they do not stop when the "UNTIL" directs the event to stop. They go on forever. Also issues when it comes when updating and adding new events that need to be set as recurring.
- There is one recurring event in the Google Calendar that is set for Wed Dec 4 2024 1:15PM to 2:15PM. Set to repeat on Wednesdays and set to stop on 12/12/24. So it is really only 2 Wednesdays 12/4 and 12/11. Scheduler is showing this event every Wednesday forever.
- Also, based on the Console output, it looks like it is pulling the information twice upon page load.
- In Postman, the api seems to show the output correctly.
- If you try to create a new event or update an event with recurrence settings, you get an error. You can add/update a non-recurring event just fine.
Below is the code. (You may see some trial code and debuging items as I have been trying to figure out why)
Any help you can provide would be greatly appriciated.
RAZOR PAGE:
@page "/gscheduler"
@rendermode RenderMode.InteractiveServer
@using Telerik.Blazor
@using Telerik.Blazor.Components
@using Telerik.Blazor.Components.Scheduler
@inject GSchedulerService GSchedulerService
<TelerikRootComponent>
<TelerikScheduler Height="600px"
@bind-Date="@StartDate"
@bind-View="@CurrView"
Data="@GSchedulerService.Appointments"
OnUpdate="OnUpdateHandler"
OnCreate="OnCreateHandler"
OnDelete="OnDeleteHandler"
AllowCreate="true"
AllowDelete="true"
AllowUpdate="true"
IdField="@(nameof(GSchedulerDto.Id))"
RecurrenceRuleField="@(nameof(GSchedulerDto.RecurrenceRule))"
RecurrenceExceptionsField="@(nameof(GSchedulerDto.RecurrenceExceptions))"
RecurrenceIdField="@(nameof(GSchedulerDto.RecurrenceId))">
<SchedulerViews>
<SchedulerDayView StartTime="@DayStart" />
<SchedulerWeekView StartTime="@DayStart" />
<SchedulerMultiDayView StartTime="@DayStart" />
<SchedulerMonthView></SchedulerMonthView>
<SchedulerTimelineView StartTime="@DayStart" />
<SchedulerAgendaView></SchedulerAgendaView>
</SchedulerViews>
</TelerikScheduler>
</TelerikRootComponent>
@code {
private DateTime StartDate { get; set; } = DateTime.Today;
private SchedulerView CurrView { get; set; } = SchedulerView.Week;
private DateTime DayStart { get; set; } = new DateTime(2000, 1, 1, 7, 0, 0);
// Initialize Scheduler Data
protected override async Task OnInitializedAsync()
{
await GSchedulerService.InitializeAppointmentsAsync();
}
// Create Event Handler
private async Task OnCreateHandler(SchedulerCreateEventArgs args)
{
if (args?.Item is GSchedulerDto newItem)
{
await GSchedulerService.CreateEventAsync(newItem);
}
}
// Update Event Handler
private async Task OnUpdateHandler(SchedulerUpdateEventArgs args)
{
if (args?.Item is GSchedulerDto updatedItem)
{
await GSchedulerService.UpdateEventAsync(updatedItem);
}
}
// Delete Event Handler
private async Task OnDeleteHandler(SchedulerDeleteEventArgs args)
{
if (args?.Item is GSchedulerDto itemToDelete && await GSchedulerService.ConfirmDeletionAsync())
{
await GSchedulerService.DeleteEventAsync(itemToDelete.Id);
}
}
}
CS file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
using Google.Apis.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration;
using Microsoft.JSInterop;
using Telerik.SvgIcons;
public class GSchedulerService
{
private readonly IConfiguration _configuration;
private readonly IJSRuntime _jsRuntime;
public List<GSchedulerDto> Appointments { get; private set; } = new();
public GSchedulerService(IConfiguration configuration, IJSRuntime jsRuntime)
{
_configuration = configuration;
_jsRuntime = jsRuntime;
}
public async Task InitializeAppointmentsAsync()
{
await LoadEventsAsync();
// await PrintEventsAsync();
}
public async Task LoadEventsAsync()
{
var service = await GetCalendarServiceAsync();
var events = await service.Events.List(_configuration["Google:CalendarId"]).ExecuteAsync();
foreach (var e in events.Items)
{
Console.WriteLine($"Event: {e.Summary}");
Console.WriteLine($"Start: {e.Start.DateTime ?? DateTime.Parse(e.Start.Date)}");
Console.WriteLine($"End: {e.End.DateTime ?? DateTime.Parse(e.End.Date)}");
Console.WriteLine($"Recurrence Rule: {e.Recurrence?.FirstOrDefault()}");
Console.WriteLine($"Recurrence ID: {e.RecurringEventId}");
Console.WriteLine($"Description: {e.Description}");
Console.WriteLine(new string('-', 40));
}
Appointments = events.Items.Select(e => new GSchedulerDto
{
Id = e.Id,
Title = e.Summary,
Description = e.Description,
Start = e.Start.DateTime ?? DateTime.Parse(e.Start.Date),
End = e.End.DateTime ?? DateTime.Parse(e.End.Date),
RecurrenceRule = FormatRecurrenceRuleForDisplay(e.Recurrence?.FirstOrDefault()),
RecurrenceId = e.RecurringEventId // Make sure to assign RecurrenceId
}).ToList();
}
public async Task PrintEventsAsync()
{
var service = await GetCalendarServiceAsync();
var events = await service.Events.List(_configuration["Google:CalendarId"]).ExecuteAsync();
foreach (var e in events.Items)
{
Console.WriteLine($"Event: {e.Summary}");
Console.WriteLine($"Start: {e.Start.DateTime ?? DateTime.Parse(e.Start.Date)}");
Console.WriteLine($"End: {e.End.DateTime ?? DateTime.Parse(e.End.Date)}");
Console.WriteLine($"Recurrence Rule: {e.Recurrence?.FirstOrDefault()}");
Console.WriteLine($"Recurrence ID: {e.RecurringEventId}");
Console.WriteLine($"Description: {e.Description}");
Console.WriteLine(new string('-', 40));
}
}
public async Task CreateEventAsync(GSchedulerDto appointment)
{
var service = await GetCalendarServiceAsync();
var newEvent = new Event
{
Summary = appointment.Title,
Description = appointment.Description,
Start = new EventDateTime
{
DateTime = appointment.Start,
TimeZone = "UTC"
},
End = new EventDateTime
{
DateTime = appointment.End,
TimeZone = "UTC"
},
Recurrence = !string.IsNullOrEmpty(appointment.RecurrenceRule)
? new List<string> { SanitizeRecurrenceRule(appointment.RecurrenceRule) }
: null
};
Console.WriteLine($"Creating event with recurrence rule: {newEvent.Recurrence?.FirstOrDefault()}");
await service.Events.Insert(newEvent, _configuration["Google:CalendarId"]).ExecuteAsync();
await LoadEventsAsync();
}
public async Task UpdateEventAsync(GSchedulerDto appointment)
{
var service = await GetCalendarServiceAsync();
var existingEvent = await service.Events.Get(_configuration["Google:CalendarId"], appointment.Id).ExecuteAsync();
existingEvent.Summary = appointment.Title;
existingEvent.Description = appointment.Description;
existingEvent.Start = new EventDateTime
{
DateTime = appointment.Start,
TimeZone = "UTC"
};
existingEvent.End = new EventDateTime
{
DateTime = appointment.End,
TimeZone = "UTC"
};
existingEvent.Recurrence = !string.IsNullOrEmpty(appointment.RecurrenceRule)
? new List<string> { SanitizeRecurrenceRule(appointment.RecurrenceRule) }
: null;
Console.WriteLine($"Updating event with recurrence rule: {existingEvent.Recurrence?.FirstOrDefault()}");
await service.Events.Update(existingEvent, _configuration["Google:CalendarId"], appointment.Id).ExecuteAsync();
await LoadEventsAsync();
}
public async Task DeleteEventAsync(string eventId)
{
var service = await GetCalendarServiceAsync();
await service.Events.Delete(_configuration["Google:CalendarId"], eventId).ExecuteAsync();
await LoadEventsAsync();
}
public async Task<bool> ConfirmDeletionAsync()
{
return await _jsRuntime.InvokeAsync<bool>("confirm", "Are you sure you want to delete this event?");
}
private async Task<CalendarService> GetCalendarServiceAsync()
{
var initializer = new ClientSecrets
{
ClientId = _configuration["Google:ClientId"],
ClientSecret = _configuration["Google:ClientSecret"]
};
var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
initializer,
new[] { CalendarService.Scope.Calendar },
_configuration["Google:User"],
CancellationToken.None);
return new CalendarService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = _configuration["Google:ApplicationName"]
});
}
private string SanitizeRecurrenceRule(string rule)
{
if (string.IsNullOrEmpty(rule))
return null;
// Normalize case and remove extra spaces
rule = rule.ToUpperInvariant().Trim();
rule = Regex.Replace(rule, @"\s+", " "); // Remove extra spaces
// Ensure the rule starts with "RRULE:"
if (!rule.StartsWith("RRULE:"))
rule = "RRULE:" + rule;
// Handle UNTIL formatting
var untilMatch = Regex.Match(rule, @"UNTIL=(\d{8}T\d{6}Z)");
if (untilMatch.Success)
{
// If UNTIL exists, ensure it is correctly formatted
var untilValue = untilMatch.Groups[1].Value;
if (!DateTime.TryParseExact(untilValue, "yyyyMMdd'T'HHmmss'Z'", null, System.Globalization.DateTimeStyles.AdjustToUniversal, out _))
{
throw new FormatException($"Invalid UNTIL format: {untilValue}. Expected format is 'YYYYMMDDTHHMMSSZ'.");
}
}
else if (!rule.Contains("COUNT=")) // If neither UNTIL nor COUNT is present, add a default UNTIL
{
var defaultUntil = DateTime.UtcNow.AddYears(1).ToString("yyyyMMdd'T'HHmmss'Z'");
rule += $";UNTIL={defaultUntil}";
}
// Optionally validate additional fields like FREQ, INTERVAL, COUNT, etc.
return rule;
}
private string FormatRecurrenceRuleForDisplay(string rule)
{
if (string.IsNullOrEmpty(rule))
return string.Empty;
// Remove the "RRULE:" prefix for display purposes
rule = rule.StartsWith("RRULE:") ? rule.Substring(6) : rule;
// Convert the UNTIL field to a human-readable format
var untilMatch = Regex.Match(rule, @"UNTIL=(\d{8}T\d{6}Z)");
if (untilMatch.Success)
{
var untilValue = untilMatch.Groups[1].Value;
if (DateTime.TryParseExact(untilValue, "yyyyMMdd'T'HHmmss'Z'", null, System.Globalization.DateTimeStyles.AdjustToUniversal, out var untilDate))
{
// Replace the UNTIL field with a readable format
rule = rule.Replace($"UNTIL={untilValue}", $"UNTIL={untilDate:yyyyMMddTHHmmssZ}");
}
}
Console.WriteLine($"Formatted Recurrence Rule: {rule}");
return rule;
}
}
public class GSchedulerDto
{
public string Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public bool IsAllDay { get; set; }
public string RecurrenceRule { get; set; }
public string RecurrenceId { get; set; }
public string RecurrenceExceptions { get; set; }
// public List<DateTime>? RecurrenceExceptions { get; set; }
public string RecurrenceType { get; set; } // Recurrence type: Daily, Weekly, Monthly, Yearly
public int Interval { get; set; } // Interval for recurrence (e.g., every X days/weeks)
public int? Count { get; set; } // Number of occurrences (optional)
public DateTime? Until { get; set; } // End date for recurrence (optional)
public string ByDay { get; set; } // Optional: Specific days (e.g., "MO,WE,FR")
public int? ByMonthDay { get; set; } // Optional: Specific day of the month
public int? ByMonth { get; set; } // Optional: Specific month
}
CONSOLE OUTPUT:
Hi!
I have a Grid inside a Tile. Sometimes, the rows increase making the Grid taller than that Tile's height. There is no visual cue when that happens. How to get scrollbars to appear in this case? The Grid doesn't sense the limitation of parent's (Tile) height and keeps increasing its heigh without showing scroll bars. In case of over-sized content, Tile should show scroll bars.