Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
151645 stories
·
33 followers

What It Takes to Fully Benefit from a Deployment Pipeline

1 Share

The Deployment Pipeline is a critical tool for ensuring software quality – and quality is a prerequisite for speed.

So, what is a Deployment Pipeline? How has it helped us improve both quality and speed? And what are, in my experience, the common problems in using and implementing deployment pipelines?

A Deployment Pipeline is a machine that helps us move from an idea to valuable software in the hands of users by organizing software development work. It enables us to go from commit to releasable outcome as quickly and efficiently as possible, in a repeatable and reliable way.

— Dave Farley, Continuous Delivery Pipelines: How to Build Better Software Faster

Before exploring how Deployment Pipelines have helped us, it is helpful to briefly introduce Continuous Integration and Continuous Delivery.

Continuous Integration

Continuous Integration is a software development practice that helps us create and maintain a high-quality codebase that is easy to change and to implement new features.

As Martin Fowler points out, the practices of Continuous Integration are:

  • Put everything in a version controlled mainline​
  • Automate the Build​
  • Make the Build Self-Testing​
  • Everyone Pushes Commits To the Mainline Every Day​
  • Every Push to Mainline Should Trigger a Build​
  • Fix Broken Builds Immediately​
  • Keep the Build Fast​
  • Hide Work-in-Progress​
  • Test in a Clone of the Production Environment​
  • Everyone can see what’s happening​
  • Automate Deployment​

    Kent Beck developed the practice of Continuous Integration as part of Extreme Programming in the 1990s. The book Continuous Integration by Paul M. Duvall, Steve Matyas, and Andrew Glover was published in 2007. While this is nothing new, many software engineers and teams still do not practice Continuous Integration and therefore miss out on the benefits it provides.

    Continuous Delivery

    Continuous Delivery can further help create and maintain high-quality software. The excellent book Continuous Delivery by Jez Humble and David Farley was published in 2010. and it describes the idea behind continuous delivery in detail, and it introduces “…the central paradigm of the book – a pattern we call the deployment pipeline“.

    Continuous Delivery is the ability to get changes of all types – including new features, configuration changes, bug fixes and experiments – into production, or into the hands of users, safely and quickly in a sustainable way.

    – https://continuousdelivery.com/

    Continuous Integration is a prerequisite for Continuous Delivery.

    The minimum activities required for Continuous Delivery, as defined by minimumcd.org, are:

    • Use Continuous Integration
    • The application pipeline is the only way to deploy to any environment​
    • The pipeline decides the releasability of changes, its verdict is definitive​
    • Artifacts created by the pipeline always meet the organization’s definition of deployable​
    • Immutable artifact (no human changes after commit)​
    • All feature work stops when the pipeline is red​
    • Production-like test environment​
    • Rollback on-demand​
    • Application configuration deploys with artifact​

    Deployment Pipeline

    The best description of deployment pipelines, and practical advice on how to implement them, I found in another excellent book Continuous Delivery Pipelines, How to Build Better Software Faster by Dave Farley. According to him, automation is the key… and is the engine that drives an effective Deployment Pipeline:

    • Test Automation​​
    • Build and Deployment Automation​​
    • Automate Data Migration​​
    • Automate Monitoring and Reporting​​
    • Infrastructure Automation​

    The simplest deployment pipeline should, after each commit, compile the code (if needed), run all unit tests, create a deployable artifact, execute acceptance tests, and enable deployment to production.

    However, a deployment pipeline can do much more, such as:

    • Static code analysis
    • Enable manual testing
    • Performance tests (e.g., latency, throughput, load)
    • Data and data migration tests
    • Security tests
    • Reliability tests etc.

    CI practices

    I see many teams/companies have a Jenkins job that compiles source code, runs (a few) unit tests, creates an artifact – often a Docker image – and have a separate deploy job/button to deploy to the dev and production environments. And they will say they are “using” CI/CD. But most of them are skipping some of the CI practices and don’t have code in an always-deployable state, even as a goal, yet still think they are “using” CI/CD.

    CI practices are not a menu where you can choose what you like and still have all the benefits CI can help you with.

    Make the build self-testing

    Test-Driven Development (TDD), or test-first development, is still an exception, and developers usually write tests after the code. Those tests are mostly unit tests (developer-oriented) and much fewer acceptance tests (user-oriented) that cover only parts of the functionalities. The test-after approach usually covers less functionality because the code is harder to test, and tests are complex to write and maintain. Such tests don’t provide fast and good-enough feedback to automate “releasability of changes” or “enable refactoring for sustained productivity.” Developers don’t trust such tests, so manual verification and team coordination are needed to decide when code can be deployed, which is never fully reliable, and more bugs reach production.

    Everyone pushes commits to the mainline every day

    Same as TDD, trunk-based development is still an exception, and almost everyone is using branches and pull requests with many PR reviews at the end of the sprint, so they are not practicing continuous integration by definition. They are missing the benefits CI brings, like “less time wasted in integration” and “enables refactoring for sustained productivity.”

    Test in a clone of the production environment​

    Some companies have a “static” copy of production, with test data already prepared in a database, where they deploy their applications and run tests. As a result, developers can’t run tests on their laptops, test preparation is complex and slow, and they regularly break other tests. Feature flags can’t be tested, and it takes a long time from commit to getting test feedback. This demotivates developers from writing tests, and over time, this negative spiral creates ‘legacy’ code that is hard to change.

    Rarely does any company I know create, on demand, a “clone of production” – with the configuration needed for a suite of tests set up exactly as in production, where the tested application is deployed the same way as in production and then tested to determine the releasability of changes.

    Usually, a Jenkins job starts the needed testcontainers during the integration test phase and tests parts of the application – or a “test” version of the application – that differ from the production application. These tests don’t cover the application artifact that will actually be deployed, nor do they test deployment, configuration, or dependencies (e.g., microservices) that aren’t properly mocked. Instead, some fake repository or adapter implementations are used that don’t exist in production.

    The application pipeline is the only way to deploy to any environment AND The pipeline decides the releasability of changes; its verdict is definitive​

    From what I’ve seen or heard, everyone has a workaround that allows deployment of artifacts that didn’t pass the Deployment Pipeline. Sometimes they create artifacts on their laptop and push them to the repository so they can be deployed. Other times, the pipeline itself allows deployment of code that hasn’t passed all required verifications.

    This can happen when a “non-important” or “flaky” test fails, or when a dependency has a critical vulnerability, etc. Usually, the team says these problems will be fixed later, when there’s time, but the new feature is considered urgent and must be deployed ASAP. In practice, the team never has time to fix these issues, workarounds become the norm, and the deployment pipeline turns into a theater.

    Keep the build fast

    With microservices being so popular, build time shouldn’t be a major problem. Yet it’s common to see builds lasting half an hour or more.

    In my experience (mostly with Java), if you create tests – unit tests, acceptance tests, etc. – that can run in parallel, it’s easy to have a deployment pipeline finish in under 5 minutes, excluding deployment. Just run everything in parallel: unit tests, acceptance tests, services with different feature flags enabled or disabled, and static code analysis.

    A Deployment Pipeline must be built, not bought

    A deployment pipeline automates your software development process and reveals both its strengths and weaknesses. When your practices are strong, the pipeline amplifies them, providing significant benefits. Conversely, if your practices are weak, the pipeline makes this very apparent – it can be difficult or even impossible to implement effectively.

    This is why Continuous Integration and Continuous Delivery are so valuable: they encourage good practices and make problems visible early. A Deployment Pipeline is not a product you can buy or outsource; you must build it yourself, leveraging various tools.

    In my experience, the best way to achieve both speed and quality in software development is to combine pair or mob programming with test-driven, trunk-based development, supported by a deployment pipeline that automates feedback on key aspects of the process. This approach provides rapid feedback, within minutes, on ideas and experiments through frequent commits and accompanying tests. It allows the codebase to evolve continuously through refactoring and enables the frequent, low-risk release of new features with minimal stress.

    The post What It Takes to Fully Benefit from a Deployment Pipeline appeared first on ShiftMag.

    Read the whole story
    alvinashcraft
    11 minutes ago
    reply
    Pennsylvania, USA
    Share this story
    Delete

    How to Build a .NET MAUI Violin Chart for Airline Delay Analysis

    1 Share

    How to Build a .NET MAUI Violin Chart for Airline Delay Analysis

    TL;DR: Struggling to interpret complex airline delay patterns? A simple bar chart won’t cut it. This guide shows how to build a .NET MAUI Violin Chart for deep statistical insights, complete with data binding and advanced customization techniques.

    Welcome to our Chart of the Week blog series!

    Comparing flight delays across multiple airlines can feel overwhelming, especially when you’re dealing with thousands of data points. Traditional charts often fail to reveal the full picture. That’s where a Violin Chart in .NET MAUI comes in.

    In this guide, you’ll learn how to build a Syncfusion® .NET MAUI Violin Charts to visualize airline arrival delay distributions. We’ll walk through transforming raw operational data into an interactive chart that highlights delay patterns, uncovers performance bottlenecks, and detects outliers, all using advanced data visualization techniques.

    What is a Violin Chart?

    A violin chart combines the power of box-and-whisker plot functionality with kernel density estimation to show the complete distribution of data. Unlike traditional bar charts or simple box plots, violin charts reveal the density and shape of data across the entire range. Wider sections indicate higher concentrations of observations, while narrower sections show lower frequencies.

    This makes violin charts ideal for:

    • Displaying multimodal distributions
    • Identifying skewness
    • Comparing distribution shapes across categories

    Here are some practical use cases:

    • Airline operations analysis: Visualize delay patterns across carriers and routes.
    • Performance benchmarking: Compare operational metrics between airlines.
    • Statistical analysis: Present experimental results or continuous variable distributions.
    • Operational excellence: Spot process variations and consistent performers.
    • Quality assurance: Monitor reliability and detect systemic issues.

    Creating a Violin Chart using .NET MAUI Charts

    To build a violin chart for airline delay analysis in .NET MAUI, we will use the SfCartesianChart with a customized BoxAndWhiskerSeries with custom rendering capabilities and advanced segment customization. This approach allows you to render violin-shaped segments and apply advanced customization for statistical visualization.

    Here’s an example of how the chart displays airline delay distributions for easy comparison and performance analysis:

    Violin chart in .NET MAUI Charts
    Violin chart in .NET MAUI Charts

    Let’s break down the steps to create a violin chart distribution using the Syncfusion .NET MAUI Cartesian Chart for analyzing the airline delay pattern.

    Step 1: Collecting airline data

    Start by loading historical airline delay data from a CSV file. For this example, we’re using 2023 flight performance data from transtats.bts.gov. Group the data by airline to prepare for distribution analysis.

    Step 2: Preparing data for visualization

    Before rendering the Violin Chart, the data must be structured using the MVVM pattern to ensure proper separation of concerns and maintainability. This process involves defining two classes: AirlineViolinModel and ViolinViewModel.

    The AirlineViolinModel class represents airline delay details. It includes properties for the airline name, which will be used as chart labels, a collection of arrival delay values for statistical analysis, and a legend color for easy visual comparison. Below is the implementation:

    public class AirlineViolinModel
    {
        public AirlineViolinModel()
        {
        }
    
        public AirlineViolinModel(string airline, List<double> arrivalDelayMinutes)
        {
            Airline = airline;
            ArrivalDelayMinutes = arrivalDelayMinutes;
        }
    
        public string Airline { get; set; }
        public Color LegendColor { get; set; }
        public List<double> ArrivalDelayMinutes { get; set; }
    }

    The ViolinViewModel class is responsible for loading, grouping, and processing the data for the chart.

    public class ViolinViewModel
    {
        public ObservableCollection<AirlineViolinModel> AirlineData { get; set; }
        public ObservableCollection<Brush> PaletteBrushesDark { get; set; }
        public ViolinViewModel()
        {
            AirlineData = new ObservableCollection<AirlineViolinModel>();
            LoadAllAirlineData();
            int count = 0;
            foreach (AirlineViolinModel airline in AirlineData)
            {
                if (PaletteBrushesDark[count++] is SolidColorBrush color)
                    airline.LegendColor = color.Color;
                }
            }
    
        private void LoadAllAirlineData()
        {
            var flightPoints = ReadCSV("Airline_Delay_Cause.csv");
            var groups = new Dictionary<string, List<double>>();
            foreach (var flightPoint in flightPoints)
            {
                if (flightPoint.Cancelled || flightPoint.Diverted) 
                    continue;
                double delay = flightPoint.ArrivalDelayMinutes;
                if (_clampEarlyToZero && delay < 0) 
                    delay = 0;
                if (_capMinutes.HasValue && delay > _capMinutes.Value) 
                    delay = _capMinutes.Value;
    
                if (!groups.TryGetValue(flightPoint.Airline, out var list))
                {
                    list = new List<double>();
                    groups[flightPoint.Airline] = list;
                }
                list.Add(delay);
            }
            var items = groups
                .Where(kvp => kvp.Value.Count >= _minSamples)
                .Select(kvp => new AirlineViolinModel(kvp.Key, kvp.Value))
                .OrderBy(m => m.Airline)
                .ToList();
    
            AirlineData.Clear();
            foreach (var item in items.Skip(4).Take(5))
            {
                AirlineData.Add(item);
            }
        }

    Step 3: Configure the Syncfusion .NET MAUI Charts

    Let’s configure the Syncfusion .NET MAUI Charts control for professional statistical data visualization using the official documentation. Use a CategoryAxis on the XAxis to display airline names, and a NumericalAxis on the YAxis to display arrival delay values in minutes.

    Code example for Quick Integration:

    <chart:SfCartesianChart>
        <chart:SfCartesianChart.XAxes>
            <chart:CategoryAxis/>
        </chart:SfCartesianChart.XAxes>
        
        <chart:SfCartesianChart.YAxes>
            <chart:NumericalAxis/>
        </chart:SfCartesianChart.YAxes>
    </chart:SfCartesianChart>

     Step 4: Customizing the Violin series

    Syncfusion .NET MAUI Charts offers advanced customization options for violin chart customization. Using custom series renderers with BoxAndWhiskerSeriesViolin and BoxAndWhiskerSegmentViolin classes.

    We create the BoxAndWhiskerSeriesViolin class that inherits from BoxAndWhiskerSeries. This class manages the overall series configuration and customization properties for professional distribution visualization:

    public class BoxAndWhiskerSeriesViolin : BoxAndWhiskerSeries
    {
        public float ViolinStrokeThickness { get; set; } = 2f;
        public float MaxHalfWidthFraction { get; set; } = 0.7f;
        public float PinchAmount { get; set; } = 0.6f;
        public float PinchSigma { get; set; } = 0.09f;
        public float WhiskerThickness { get; set; } = 2.2f;
        public float IqrBarWidthFraction { get; set; } = 0.08f;
        public float IqrBarHeightFraction { get; set; } = 0.70f;
        public float IqrBarMinHeightPixels { get; set; } = 8f;
        public float IqrFillOpacity { get; set; } = 0.35f;
        public float TipGapPixels { get; set; } = 7f;
        public float WhiskerShortenPixels { get; set; } = 6f;
        public float WhiskerLengthFraction { get; set; } = 0.55f;
        public float MinWhiskerPixels { get; set; } = 7f;
        protected override ChartSegment CreateSegment()
        {
            return new BoxAndWhiskerSegmentViolin(this);
        }
    }

    Then create the BoxAndWhiskerSegmentViolin class that inherits from BoxAndWhiskerSegment. Override the Draw method to render the distinctive arc-shaped violin body along with whiskers and statistical indicators for comprehensive airline delay analysis.

    public class BoxAndWhiskerSegmentViolin : BoxAndWhiskerSegment
    {
        private readonly BoxAndWhiskerSeriesViolin _owner;
        private float _left, _right, _top, _bottom;
        private float _centerX, _halfWidth;
        private float _yQ1, _yQ3, _yMedian;
        public BoxAndWhiskerSegmentViolin(BoxAndWhiskerSeriesViolin owner)
        {
            _owner = owner;
        }
        protected override void OnLayout()
        {
            base.OnLayout();
            _left = Left;
            _right = Right;
            _top = Top;
            _bottom = Bottom;
            if (float.IsNaN(_left) || float.IsNaN(_right) || float.IsNaN(_top) || float.IsNaN(_bottom))
                return;
    
            _centerX = (_left + _right) * 0.5f;
            _halfWidth = MathF.Max(1f, (_right - _left) * 0.5f);
            _yQ1 = _top;
            _yQ3 = _bottom;
            _yMedian = (_yQ1 + _yQ3) * 0.5f;
        }
        protected override void Draw(ICanvas canvas)
        {
            if (float.IsNaN(_left) || float.IsNaN(_right) || float.IsNaN(_top) || float.IsNaN(_bottom))
                return;
    
            DrawViolin(canvas);
            DrawVerticals(canvas);
        }
    }

    Step 5: Design the Violin Chart layout

    To design the violin chart, add multiple instances of the BoxAndWhiskerSeriesViolin class. Configure each series with the appropriate ItemsSourceXBindingPath, and YBindingPath properties. Ensure that the ViolinViewModel is set as the chart’s BindingContext for seamless data visualization.

    Here’s how you can do it in code.

    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:chart="clr-namespace:Syncfusion.Maui.Charts;assembly=Syncfusion.Maui.Charts" 
                 xmlns:local="clr-namespace:ViolinChart"
                 x:Class="ViolinChart.MainPage">
        <chart:SfCartesianChart>
            <local:BoxAndWhiskerSeriesViolin ItemsSource="{Binding AirlineData}"
                                                                          XBindingPath="Airline"
                                                                          YBindingPath="ArrivalDelayMinutes"/>
        </chart:SfCartesianChart>
    
    </ContentPage> 

    Step 6: Customize the Chart

    Now, let’s make the chart visually engaging and easy to interpret. You’ll customize the title, axes, legend, and series styling to create a polished, developer-friendly visualization.

    Chart title

    Set a descriptive title that clearly communicates the purpose of the chart. A well-defined title helps users quickly understand the airline delay distribution being analyzed.

    <StackLayout Grid.Row="0" Grid.Column="1"  
                         Spacing="3" 
                         VerticalOptions="Center" 
                         Margin="15,0,10,0"> 
        <Label Text="Airline Arrival Delay Distribution Analysis"  
                       FontSize="{OnPlatform Android=16, Default=20, iOS=16}"  
                       FontAttributes="Bold"  
                       TextColor="#1A1A1A" 
                       LineBreakMode="WordWrap"/> 
        <Label Text="Comprehensive Distribution Comparison of Arrival Delays Across Major Airlines"  
                       FontSize="{OnPlatform Android=11, Default=13, iOS=11}"  
                       TextColor="#64748B" 
                       FontAttributes="None" 
                       LineBreakMode="WordWrap"/> 
    </StackLayout>

    Chart axes

    Fine-tune the X-axis and Y-axis for clarity:

    • Use meaningful titles and label styles.
    • Define ranges for better precision.
    • Control visibility with the IsVisible property. These adjustments ensure the chart delivers accurate and readable statistical insights.

    Here’s how you can do it in code.

    <!-- X-Axis Configuration -->
    <chart:SfCartesianChart.XAxes>
        <chart:CategoryAxis ShowMajorGridLines="False"
                            EdgeLabelsDrawingMode="Shift"
                            AxisLineStyle="{chart:ChartLineStyle Stroke=#233042, StrokeWidth=1}">
            <chart:CategoryAxis.LabelStyle>
                <chart:ChartAxisLabelStyle FontSize="{OnPlatform Default=11, Android=10}"/>
            </chart:CategoryAxis.LabelStyle>
        </chart:CategoryAxis>
    </chart:SfCartesianChart.XAxes>
    
    <!-- Y-Axis Configuration -->
    <chart:SfCartesianChart.YAxes>
        <chart:NumericalAxis Interval="40" Minimum="-10" ShowMajorGridLines="False">
            <chart:NumericalAxis.Title>
                <chart:ChartAxisTitle Text="Arrival Delay (Minutes)"/>
            </chart:NumericalAxis.Title>
            <chart:NumericalAxis.AxisLineStyle>
                <chart:ChartLineStyle Stroke="#233042" StrokeWidth="1"/>
            </chart:NumericalAxis.AxisLineStyle> 
            <chart:NumericalAxis.LabelStyle>
                <chart:ChartAxisLabelStyle TextColor="#9CA3AF" FontSize="{OnPlatform Default=11, Android=10}"/>
            </chart:NumericalAxis.LabelStyle>
        </chart:NumericalAxis>
    </chart:SfCartesianChart.YAxes>

    Violin series styling

    Enhance the violin series by customizing colors, opacity, stroke widths, and statistical indicators. Proper styling enhances the chart’s visual appeal and facilitates a more effective multi-carrier comparison.

    <chart:SfCartesianChart> 
        <local:BoxAndWhiskerSeriesViolin ItemsSource="{Binding AirlineData}"
                                         XBindingPath="Airline"
                                         YBindingPath="ArrivalDelayMinutes"
                                         PaletteBrushes="{Binding PaletteBrushesDark}"
                                         ShowOutlier="True"
                                         ShowMedian="True"
                                         StrokeWidth="1.6"
                                         Opacity="0.92"/>
    </chart:SfCartesianChart> 

    Legend

    Add a legend with color-coded indicators, enhancing the multi-carrier comparison capabilities of the distribution visualization, as shown below:

     <Grid Grid.Row="1" Padding="{OnPlatform Default='4,8,4,2', Android='2,6,2,2', iOS='2,6,2,2'}">
        <CollectionView ItemsSource="{Binding AirlineData}"  
                        HorizontalScrollBarVisibility="Never" 
                        HorizontalOptions="CenterAndExpand"
                        VerticalScrollBarVisibility="Never">
            <CollectionView.ItemsLayout>
                <LinearItemsLayout Orientation="Horizontal" ItemSpacing="10"/>
            </CollectionView.ItemsLayout>
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Grid ColumnDefinitions="Auto, *" Padding="0" Margin="0,0,6,8">
                        <!-- Color Swatch -->
                        <RoundRectangle Grid.Column="0"  
                                       WidthRequest="{OnPlatform Android=10, Default=20, iOS=10}" 
                                       HeightRequest="{OnPlatform Android=10, Default=20, iOS=10}" 
                                       BackgroundColor="{Binding LegendColor}" 
                                       Margin="8, 0,5,0"/>
                        <!-- Airline Label -->
                        <Label Grid.Column="1"
                               Text="{Binding Airline}"
                               Margin="6,0,0,0"
                               VerticalOptions="Center" 
                               FontSize="{OnPlatform Default=12, Android=11, iOS=12}"/>
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </Grid>

    Once you run the code, the chart renders a clear and visually compelling view of airline delay distributions, making it easy to compare performance across carriers.

    .NET MAUI Violin Chart displaying airline delay analysis
    .NET MAUI Violin Chart displaying airline delay analysis

    GitHub reference

    For more details and complete source code for this .NET MAUI Violin Chart implementation, refer to the GitHub demo.

    Supercharge your cross-platform apps with Syncfusion's robust .NET MAUI controls.

    Conclusion

    Thank you for reading! In this guide, we’ve explored how to create a professional .NET MAUI custom Violin Chart to visualize airline arrival delay distribution patterns and statistical analysis across major airlines using Syncfusion .NET MAUI Charts. This advanced data visualization approach provides aviation professionals with powerful tools for distribution analysis, performance benchmarking, and operational excellence.

    If you’re a Syncfusion user, you can download the setup from the  license and downloads page. Otherwise, you can download a free a 30-day free trial.

    You can also contact us through our support forumsupport portal, or feedback portal for queries. We are always happy to assist you!

    Read the whole story
    alvinashcraft
    11 minutes ago
    reply
    Pennsylvania, USA
    Share this story
    Delete

    Introducing the Blazor Block Editor: A Modern Block-Based Editing Experience (2025 Volume 4)

    1 Share

    Introducing the Blazor Block Editor: A Modern Block-Based Editing Experience (2025 Volume 4)

    TL;DR: The Blazor Block Editor, introduced in the 2025 Volume 4 release, brings a modern block-based approach to content editing. It offers flexible blocks, slash commands, event-driven customization, and built-in accessibility. This blog shows you how to get started and make the most of its features.

    Building rich, structured content in web apps has always been a challenge. Traditional editors often feel clunky, lack flexibility, and make customization a painful process. The Block Editor, introduced in the 2025 Volume 4 release for Blazor, is a modern, block-based editing experience designed for developers who want power, simplicity, and control.

    In this guide, you’ll learn:

    • What makes the Block Editor powerful?
    • How to integrate it into Blazor Server and Blazor WebAssembly apps.
    • How to customize it using events.
    • Best practices for security, accessibility, and localization.

    Note: The Syncfusion Blazor Block Editor component was introduced in the 2025 Volume 4 release as a preview.

    What is the Block Editor?

    The Block Editor organizes content as an ordered list of blocks, such as headings, paragraphs, lists, checklists, quotes, callouts, dividers, tables, images, and toggle/accordion blocks. Each block has its own schema and editable state, allowing for fine-grained control over layout, formatting, and behavior.

    The key features are;

    • Multiple block types: Headings (levels 1–4), paragraphs, ordered/unordered lists, checklists, quotes, callouts, dividers, tables, images, toggle blocks, and more.
    • Slash commands (/): Quickly insert or transform content blocks using an inline command menu.
    • Rich text formatting: Bold, italic, underline, strikethrough, uppercase, and inline code.
    • Action menu: Block-level operations like move, delete, and duplicate.
    • Context menu: Right-click access to common block actions.
    • Inline content: Insert links, labels, and mentions directly within blocks.
    • Copy & paste with formatting: Preserve styles and structure when pasting from Microsoft Word, Outlook, or other editors.
    • Undo/redo: Robust history stack for controlled editing.
    • Events-driven customization: Hook into events like block add/remove/update, selection change, command execution, paste, and mention selection.
    • Accessibility (WCAG 2.0): Full keyboard navigation, roles, and ARIA for assistive technologies.
    • Localization: Built-in localization support to adapt UI and messages to various languages.
    • XSS prevention: Built-in sanitization and safe content handling.

    Quick start: Add Block Editor to a Blazor App

    The official Getting Started documentation provides the most accurate and detailed instructions. Here’s a quick overview:

    Step 1: Install the NuGet package

    Start by installing the Syncfusion.Blazor.BlockEditor NuGet package for the Block Editor. This package automatically includes required dependencies.

    Step 2: Register Syncfusion services

    In your Program.cs file, register Syncfusion Blazor services.

    builder.Services.AddSyncfusionBlazor();

    Step 3: Add a Syncfusion theme

    Include a Syncfusion theme in your layout file (e.g., _Host.cshtml or Shared/_Layout.cshtml).

    <link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />

    Step 4: Add the Block Editor component

    Place the Block Editor component in your Razor page:

    <SfBlockEditor Height="700px" Blocks="@BlockDataOverview">
    </SfBlockEditor>
    
    @code {
        private List<IBlock> BlockDataOverview { get; set; } = new EditorBlockData().GetBlockDataOverview();
    
        public class EditorBlockData
        {
            public List<IBlock> GetBlockDataOverview()
            {
                return new List<IBlock>
                {
                    new HeadingBlock
                    {
                        Level = HeadingLevel.Level4,
                        Content = new List<IContent> { new TextContent { Text = "💫 Overview" } }
                    },
                    new ParagraphBlock
                    {
                        Content = new List<IContent>
                        {
                            new TextContent { Text = "Provide project background, core objectives, key stakeholders, and proposed timeline here — include an inspiring quote to set the tone." }
                        }
                    },
                    new ParagraphBlock(), // Empty paragraph as spacer
    
                    // Goals Section
                    new HeadingBlock
                    {
                        Level = HeadingLevel.Level4,
                        Content = new List<IContent> { new TextContent { Text = "🎯 Goals" } }
                    },
                    new ParagraphBlock
                    {
                        Content = new List<IContent>
                        {
                            new TextContent { Text = "List the primary project goals and desired outcomes." }
                        }
                    },
                    new NumberedListBlock
                    {
                        // Start with empty items; users can add via the editor
                        Items = new List<ListItem>()
                    },
                    new ParagraphBlock(), // Spacer
    
                    // Team Members Section
                    new HeadingBlock
                    {
                        Level = HeadingLevel.Level4,
                        Content = new List<IContent> { new TextContent { Text = "🧑‍💻 Team Members" } }
                    }
                };
            }
        }
    }

    Here’s a quick demo of the Blazor Block Editor in action.

    Blazor Block Editor
    Blazor Block Editor

    Note: For detailed guidance on customization, toolbar configuration, and custom blocks, refer to the official documentation. Explore live Block Editor demos for hands-on examples.

    Conclusion

    Thank you for reading. The Syncfusion Blazor Block Editor represents a significant advancement in structured content editing for Blazor applications. With its intuitive block-based approach, rich feature set, and seamless integration, it empowers developers to build sophisticated, user-friendly editors for a wide range of use cases. As a preview component in the 2025 Volume 4 release, it lays the foundation for even more powerful tools in future updates. Start experimenting with it today to elevate your content creation experiences!

    For a hands-on look, explore our online demos and documentation of the Blazor Block Editor. You can check out our Release Notes and What’s New for the other updates in this release and provide your feedback in the comments below.

    If you’re a Syncfusion user, you can download the setup from the license and downloads page. Otherwise, you can download a free 30-day trial.

    Your experience matters to us. Please share your thoughts on the Block Editor:

    • How easy was it to create and edit content using the Block Editor?
    • What features or enhancements would you like next (e.g., drag-and-drop improvements, new block types, advanced formatting)?
    • Did you face any challenges or have suggestions for improvement?

    You can share your feedback in the comments below or contact us through our support forumsupport portal, or feedback portal for any queries. We are always happy to assist you!

    Read the whole story
    alvinashcraft
    12 minutes ago
    reply
    Pennsylvania, USA
    Share this story
    Delete

    Why does my Ctrl+M accelerator key activate when I press the Enter key?

    2 Shares

    A customer didn’t understand why their Win32 accelerator key for Ctrl+M was triggering spuriously. Specifically, it was triggering when the user hit the Enter key, which is nothing like the Ctrl+M two-key combination.

    They defined their accelerator table like this:

    IDA_MAIN ACCELERATORS
    BEGIN
    "^M", IDM_MUMBLE
    END
    

    Accelerator key definitions can be done by character or by virtual key code. If you use a quoted string, then you are defining a character accelerator which triggers when that character is entered by whatever means. For example, if you define a character accelerator for "0", it will trigger if the user presses the 0 key on the top row of the keyboard, or if they press the Numpad0 key on the numeric keypad, or even if they type Alt+Numpad4,Numpad8 to type the character by entering its character code on the numeric keypad.

    In the above case, the accelerator was defined as the character ^M, which is shorthand for Ctrl+M, or character code 13. There are multiple ways to enter that character code. You could type Ctrl+M on the keyboard, or you could press the Enter key.

    If you want the accelerator to trigger only for the case of Ctrl+M, then you want to define a virtual key accelerator, not a character accelerator.

    IDA_MAIN ACCELERATORS
    BEGIN
    "M", IDM_MUMBLE, CONTROL, VIRTKEY
    END
    

    Virtual key accelerators trigger only when the specified keys are pressed. In our case, we want the M virtual key in combination with the CTRL key.

    Conversely, if you want the accelerator to trigger only for the Enter key, you would specify that key as a virtual key.

    IDA_MAIN ACCELERATORS
    BEGIN
    VK_RETURN, IDM_MUMBLE, VIRTKEY
    END
    

    There are two more key-to-control-character combinations that you may stumble across.

    • Backspace becomes the character ^H.
    • Tab becomes the character ^I.

    Technically, there is a third combination:

    • Esc becomes the character ^[.

    However, the Resource Compiler does not accept "^[" as a control key, so you aren’t going to run into that one by mistake.

    The post Why does my <KBD>Ctrl</KBD>+<KBD>M</KBD> accelerator key activate when I press the <KBD>Enter</KBD> key? appeared first on The Old New Thing.

    Read the whole story
    alvinashcraft
    12 minutes ago
    reply
    Pennsylvania, USA
    Share this story
    Delete

    Reactive Extensions for .NET Status and Plans for .NET 10

    1 Share

    Ian Griffiths, Technical Fellow at endjin, shares the latest updates on the Reactive Extensions for .NET (AKA ReactiveX AKA Rx.NET). Learn about the new features in Rx 6.1, what .NET 10 means for the project, and the significant packaging changes coming in Rx v7.0 that finally solve the long-standing deployment bloat issue.

    In this talk:

    • Rx 6.1 New Features — DisposeWith operator for fluent CompositeDisposable usage, new TakeUntil overload with cancellation token support, and ResetExceptionDispatchState operator
    • The Bloat Problem Explained — Why self-contained Windows deployments were pulling in 90MB of unnecessary WPF and Windows Forms assemblies
    • Rx 7 Preview — How the new packaging model fixes bloat while maintaining source and binary compatibility
    • Community Contributions — Features from Chris Pullman (ReactiveUI), Neils Berger, Daniel Weber, and Adam Jones
    • Async Rx .NET — Status update and plans for a non-alpha release

    Hi! Thanks for listening to this talk about what's been happening lately with the Reactive Extensions for .NET and what we've got planned. My name's Ian Griffiths. I'm a technical fellow at endjin, and my employer endjin maintains the Reactive Extensions for .NET. You can find the source code in this repo on GitHub.

    If you're listening to this talk, it's likely that you already know about the Reactive Extensions, or as we usually call them, just Rx. But just in case you don't, here's a very quick introduction. Rx is an event-driven programming model. It's useful in any application where things happen. It provides a functional declarative programming style for writing code that responds to events.

    This model has become popular in other programming languages, especially JavaScript, but it was originally a .NET technology invented by Microsoft. Rx .NET was one of the first projects that Microsoft open sourced, one of the first to come under .NET Foundation ownership, and it's been a community-supported project for over a decade now.

    Today I'm gonna talk about things that have been, things that are, and some things that have not yet come to pass. More specifically, I'll talk about Rx 6.1, our most recent release. I'll also talk about what .NET 10 means for Rx and also for the related Ix project that lives in the same repository.

    There's a new feature in the .NET 10 runtime class libraries that has a significant impact on us. And finally, I'll talk about our progress towards the next release, Rx version 7.0. To put this all in context, it's useful to know the recent history of Rx. endjin took over maintenance at the start of 2023.

    This was a little over two years since there had last been a version since Rx five. The project had ground to a halt because its previous maintainers were no longer able to devote much time to it. Our first job was to bring the codebase back into line with current tooling, 'cause it had fallen behind and wasn't actually able to build on the current version of Visual Studio.

    So we addressed that and then went on to add tests for newer versions of .NET, and then produced a new release, Rx version six. We spent a big chunk of the next year bringing the documentation up to date in the form of the free online ebook, Intro to Rx. We also spent a lot of time working out how best to solve a problem that I'll be talking about later, because that work will not ship until Rx 7.0.

    So there wasn't a lot of visible progress. We had a couple of minor bug fix releases, but it wasn't until last month that we produced a release with any new features. And that's what I want to talk about first: Rx version 6.1, which shipped in October 2025. We've bumped the minor version number because there are three new features in Rx 6.1.

    All of these arose from community input. Two were written by community members, and the third was based on community suggestions. So we have a new DisposeWith operator, which enables a fluent programming style when working with the CompositeDisposable type. This comes from Chris Pullman, a major contributor to the ReactiveUI project, and this extension fits in well with common coding idioms in that world.

    To show how this works, I've got a program here based on one of the ReactiveUI examples. It's a very simple front end, and I can type search terms in here and it finds packages for me. This happens to be a WPF app, and as you can see, the main window here has this text box for me to type into and a list box that shows the result.

    The basic idea with ReactiveUI is that we can represent user input as Rx observable streams, and we can also direct the output of observable streams into user interface elements. This code here is the basic logic for handling search input, and it's mostly standard Rx operators. This first method is from ReactiveUI and it lets me get an observable stream representing input to my textbox.

    We're then using Rx's Throttle to ensure we don't perform searches too often while the user types, and this Where clause filters out empty inputs. And ultimately this delivers results into another property. And so that's one of the basic ideas of the ReactiveUI library. It uses Rx to define how information flows through the application.

    Now if we look at the actual main window, this is where the code that you just saw gets hooked up to the user interface elements. You can see that this connects the application logic's search term input to the actual text box, and this connects the search logic's output to an actual list box. And this code here is where the new feature that we've added in Rx 6.1 comes in useful.

    UI elements get created and torn down all the time. Entries in the search results, for example, appear when I type things and get replaced when I type something else. So each time some new UI opens up, not only do we need to run this sort of code to connect everything up, we also need to be ready to shut it all down cleanly. To enable that, this WhenActivated method passes me this argument, which uses Rx's CompositeDisposable type. Now that's been in Rx for years and it's just a collection of IDisposable objects, the idea being that they can all be disposed at once when needed. Any disposable objects I put in here will automatically be disposed when this user interface element goes away.

    Right now I'm just calling Add to add things to that CompositeDisposable, and that's okay, but it's not quite the normal style for a ReactiveUI app. Normally we chain method calls together one after another in what's often called the fluent style. And if I show you another UI element, the one for an individual search result, you can see it's using that CompositeDisposable slightly differently.

    Instead of wrapping each of these setup lines in a call to Add, I've got just one more fluent invocation on the end of it with this DisposeWith method. It's a small change, but it enables teardown to be handled slightly more neatly. And that's Rx 6.1's new DisposeWith feature. We also have a new overload of the TakeUntil operator.

    This comes from Neils Berger and incorporates feedback from Daniel Weber, both members of the Rx community. Existing overloads enable a sequence to be observed until either an element matches some criteria or some other observable source completes. But this now enables a cancellation token to signal the instant at which the sequence should complete.

    Finally, we have a new operator called ResetExceptionDispatchState, developed in response to feedback from Adam Jones, and this one is best shown by example. Rx has always offered this Throw operator. When you subscribe to the observable it returns, it immediately calls OnError, passing this exception. Since we construct just a single exception, it will reuse it for each subscription.

    Normally that's not a problem, but there's one situation in which this can produce surprising behavior. But before I show you that situation, I want to demonstrate something about exceptions that has nothing to do with Rx and which you might not be aware of. I've got a different program here that creates a single exception object, and then each time around this loop, we wrap that in a task and await the task. That await will, of course, throw the exception, and this C# catch block just displays it. But watch what happens when I run this. Each time around the loop, my stack trace gets longer and longer. This is a feature of how the .NET runtime throws asynchronous exceptions. Each throw appends the current location to the stack trace, and normally that's what we want.

    If an exception has traveled through multiple await statements before reaching a catch block, we want the stack trace to reflect that whole history, which is why the asynchronous rethrow appends new information to the existing stack trace. But this mechanism assumes that when the error occurred, something did actually use a normal throw operation.

    And finally, there are a few changes that are minor, but which are technically breaking changes that have been waiting for the next major release. So we made a small change to nullability handling of the OfType operator to align it with some other LINQ implementations. And there are some behaviors that we consider to be unintended and where we think the fix will align with how people expect things to work, but technically it's a breaking change. And finally, we're gonna stop producing new versions of the old compatibility facades that were introduced in Rx version four.

    But the big one is the fix for the bloat issues. We are going to introduce a significant change in Rx .NET packaging. It will only affect people building UI applications on Windows, though. I've got a console application with a Windows-specific target framework. It needs that because it invokes certain Windows APIs. The Program.cs here, I'm using the network availability API provided by WinRT, and I've wrapped it using Rx to provide a stream of notifications when the computer loses or acquires network connections. That's a Windows-specific API, but this is just a console app.

    So this illustrates that just because you specified a Windows target framework, it doesn't necessarily mean you are building a classic desktop application with a user interface. But look at this build output folder. Now, I've configured this project to use self-contained deployment, meaning that it brings its own copy of the .NET runtime and any other components that it needs to run.

    This application can be installed by simply copying this entire folder to the target machine. It does not need the .NET runtime to be pre-installed, but it's huge. Now part of that is simply that the .NET runtime is quite large, but that's not the whole story. This is much bigger than a self-contained console app would normally be.

    Looking more closely, we see these PresentationFramework components. These are part of WPF, one of the Windows desktop frameworks that .NET offers. And a little further down, we can see that the other one, Windows Forms, is here too. These two UI frameworks are adding about 90 megabytes to the size of this folder.

    We can mitigate this a little by enabling trimming. That will make the whole folder a great deal smaller, but even so, it ends up being many megabytes larger than it would've been if these UI frameworks weren't here. So why are they here? It's because of the unfortunate consequences of a design decision made back in Rx version four.

    This decision is known as "the great unification," and it took us through a world in which Rx consisted of multiple components in which it was kind of unclear what they all did, and even more unclear which ones were required in which situations, and it took us into a world where there's exactly one component: System.Reactive. And at the time, this was great, but the decision to include UI framework support as part of this great unification has turned out to be problematic. The effect is that if you build an application with a target in which Windows Forms and WPF are available, Rx will provide its Windows Forms and WPF support, and that's a problem because to do that, it adds an implicit reference to the desktop UI framework, meaning that self-contained deployments get a copy of these UI frameworks even if the application itself isn't actually using them.

    I'm now gonna upgrade this project to a preview build of Rx version 7.0 and rebuild it. Now the project appears to work exactly as before, and our goal is that for anyone who wasn't building WPF, Windows Forms, or UWP applications, that it all carries on working exactly as before.

    So if you weren't running into this bloat issue, or perhaps because you weren't using self-contained deployment, then this shouldn't really affect you. But this example does use it, so let's look at the output folder. This is now much smaller. This is almost exactly the same size as a simple Hello World application, made slightly larger just by the presence of the System.Reactive assembly, but those PresentationFramework and Windows Forms assemblies are gone.

    So you can see that the main effect has been achieved, but this has consequences for applications that actually are using the UI framework features. So here I've got a WPF project that is using Rx six, and this line of code here is using a WPF-specific feature. Specifically, this ensures that any events that emerge from this observable are delivered via the correct thread for this window.

    I'm now gonna try upgrading this to Rx 7.0.

    Now I've got compiler errors. To enable Windows projects to use Rx without being forced to have a dependency on the desktop UI frameworks, we've had to hide the relevant types. They are still actually present at runtime to ensure binary compatibility, but they are effectively invisible at build time. I could spend well over an hour explaining why this was the least bad available solution to the problem, but we don't have time for that in this particular talk. Anyway, notice that in addition to the error, we've got this diagnostic. Now we've added a code analyzer to Rx 7.0 that detects this exact situation and tells you what you need to do to fix it.

    We've done this because we know people will get this error when they upgrade and it isn't entirely obvious what to do about it. So the analyzer tells me that I need to add a reference to the new System.Reactive.WPF NuGet package. So let's go back and find that. And when I add this, I'm back in business.

    So this provides source-level compatibility as long as you add the new package reference. And as I mentioned, we do actually provide binary-level compatibility by using a trick with reference assemblies to hide these types just at build time. So the need to add a new package is slightly annoying, but our view is that UI framework-specific support should really always have been an opt-in feature.

    So this gets us finally to the place where we want to be. Now you might be wondering why it's taken so long to get here. I've been talking about this for two years now. We first opened a GitHub discussion on this back in November 2023, and we did produce a prototype just a few months later to show what it would look like in the hope of getting some feedback.

    Now, we didn't get a lot, so we announced in October 2024 that we were gonna move forward, but that did produce some negative community feedback. The plan we had at the time was somewhat more radical and would've been more disruptive. Its end state would've avoided the weird trick we've had to use with reference assemblies.

    And it would also have enabled us finally to remove the UAP target framework from the main Rx package. But it would also have created a lot of problems for people who weren't actually affected by the issue we were trying to fix. So we had a rethink and came up with a new plan. The critical difference is that now System.Reactive remains as the main assembly, meaning that a lot of Rx users shouldn't even notice this change.

    We also introduced an extensive set of tests designed to find the kind of subtle problems that emerge with any attempt to fix this issue. We called that test suite "Rx Gauntlet," and that is what's given us the confidence to move forward with this new plan.

    One last thing: last year I talked about Async Rx .NET. We have made a little progress on extending the test suite, but we considered the code bloat issue with Rx .NET to be of higher priority. We will be getting back to Async Rx .NET, and we hope to have a non-alpha release next year.

    My name's Ian Griffiths. Thanks for listening.

    Read the whole story
    alvinashcraft
    12 minutes ago
    reply
    Pennsylvania, USA
    Share this story
    Delete

    The 10 Biggest AI Stories of 2025

    1 Share
    From: AIDailyBrief
    Duration: 24:56
    Views: 271

    This episode covers the 10 biggest AI stories of 2025—how DeepSeek’s R1 shook markets, pushed reasoning into the mainstream, and intensified the US–China model race. It also breaks down the massive AI infrastructure boom (Stargate, hyperscaler capex, energy + data centers), the nonstop AI bubble debate, and what enterprise adoption really looked like behind the headlines. Finally, it explores the rise of vibe coding, the standardization of agent infrastructure (MCP, agent-to-agent protocols, context engineering), and the “next leap” models—Gemini 3, Opus 4.5, and GPT-5.2—setting the tone for 2026.

    Brought to you by:
    KPMG – Go to ⁠www.kpmg.us/ai⁠ to learn more about how KPMG can help you drive value with our AI solutions.
    Vanta - Simplify compliance - ⁠⁠⁠⁠⁠⁠⁠https://vanta.com/nlw

    The AI Daily Brief helps you understand the most important news and discussions in AI.
    Subscribe to the podcast version of The AI Daily Brief wherever you listen: https://pod.link/1680633614
    Get it ad free at
    Join our Discord: https://bit.ly/aibreakdown

    Read the whole story
    alvinashcraft
    13 minutes ago
    reply
    Pennsylvania, USA
    Share this story
    Delete
    Next Page of Stories