Ticket T1050385
Visible to All Users

Blazor - How to update a layout item text (Caption of a property editor) based on property value changes

created 3 years ago (modified 3 years ago)

[DevExpress Support Team: CLONED FROM Q514530: How to change a DetailView editor or ListView column caption dynamically]

Hello,

When i implemet https://supportcenter.devexpress.com/ticket/details/t601244/how-to-change-property-caption-at-runtime-based-on-another-property-value solution to my Blazor project always show "Unsaved changes will be lost…" message?

C#
void ObjectSpace_ObjectChanged(object sender, ObjectChangedEventArgs e) { var obj = this.View.CurrentObject as OrderSlip; if (obj != null) { if (e.PropertyName == "StoppageRate") { var _peditor_stoppagerate = (View as DetailView).FindItem("StoppageAmount"); if (_peditor_stoppagerate != null) { if (obj.StoppageRate == enumStoppageRates.None) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT"); else if (obj.StoppageRate == enumStoppageRates.s10) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT") + " (%10)"; else if (obj.StoppageRate == enumStoppageRates.s15) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT") + " (%15)"; else if (obj.StoppageRate == enumStoppageRates.s17) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT") + " (%17)"; else if (obj.StoppageRate == enumStoppageRates.s2) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT") + " (%2)"; else if (obj.StoppageRate == enumStoppageRates.s20) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT") + " (%20)"; else if (obj.StoppageRate == enumStoppageRates.s3) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT") + " (%3)"; else if (obj.StoppageRate == enumStoppageRates.s4) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT") + " (%4)"; else if (obj.StoppageRate == enumStoppageRates.s5) _peditor_stoppagerate.Caption = DevExpress.ExpressApp.Utils.CaptionHelper.GetLocalizedText("eKolayBu_Texts", "LBL_STOPPAGEAMOUNT") + " (%5)"; } View view = Frame.View; if (Frame.SetView(null, true, null, false)) { view.LoadModel(false); Frame.SetView(view); } }
Comments (3)
Dennis Garavsky (DevExpress) 3 years ago

    Hello, Kartal.

    It is incorrect to call Frame.SetView from within the ObjectChanged event handler. Frame.SetView is a very serious operation that destroys and recreates the whole View controls and related Controllers. You are trying to do this in the middle of some logic in your custom Controller. So, this glitch is expected with your custom code, especially because you do not commit your existing changes manually and you do not unsubscribe from events. Even if you do commit changes and unsubscribe from events, recreating View controls and Controllers is a very costly operation performance wise (if it is executed on each property change).

    We recommend using Frame.SetView only in rare usage scenarios, such as applying Application Model changes to the current View immediately or assigning a new View to an existing window.

    Normally, captions are updated when View creates controls for the first time (example). If this does not meet your needs and you require more frequent updates, please describe your usage scenario and expected results from an end-user perspective. With this information, we will be able to find alternative solutions (if possible). It might be possible to achieve your ultimate goal from another perspective with different technical solutions. Thanks.

      Hello Dennis,

      I want to change view item captions by conditionally. I want to change some property captions when user change some values on a view. Please look at the attached file.

      Dennis Garavsky (DevExpress) 3 years ago

        Hello, Kartal. Thank you for your video. Your scenario is now clear and makes sense to me. I hope that we will able to find another solution with direct customization of DxFormLayout items in code or other techniques with JavaScript (rather than setting ViewItem's Caption property). Please bear with us.

        Answers

        created 3 years ago (modified 2 years ago)

        Hello,

        v22.2.5+

        The DxFormLayout component and its items are accessible for customization. The BlazorLayoutManager.ItemCreated event is triggered when a layout item model is created. By handling this event, you can manage the properties of the following components: DxFormLayout, DxFormLayoutTabPages, DxFormLayoutTabPage, DxFormLayoutGroup, and DxFormLayoutItem.

        You can check how to use the BlazorLayoutManager.ItemCreated event in the following examples:

        This example demonstrates how to update the layout item's caption based on another property value:

        C#
        using DevExpress.ExpressApp; using DevExpress.ExpressApp.Blazor.Components.Models; using DevExpress.ExpressApp.Blazor.Editors; using DevExpress.ExpressApp.Blazor.Layout; using MainDemo.Module.BusinessObjects; namespace MainDemo.Blazor.Server.Controllers; public class TestViewController : ObjectViewController<DetailView, Employee> { private DxFormLayoutItemModel officeLayouItemModel; protected override void OnActivated() { base.OnActivated(); View.ObjectSpace.ObjectChanged += ObjectSpace_ObjectChanged; View.CurrentObjectChanged += View_CurrentObjectChanged; ((BlazorLayoutManager)View.LayoutManager).ItemCreated += TestViewController2_ItemCreated; } override protected void OnDeactivated() { View.ObjectSpace.ObjectChanged -= ObjectSpace_ObjectChanged; View.CurrentObjectChanged -= View_CurrentObjectChanged; ((BlazorLayoutManager)View.LayoutManager).ItemCreated -= TestViewController2_ItemCreated; } private void TestViewController2_ItemCreated(object sender, BlazorLayoutManager.ItemCreatedEventArgs e) { if(e.ModelLayoutElement.Id == $"{nameof(Department)}.{nameof(Department.Office)}" && e.LayoutControlItem is DxFormLayoutItemModel model) { officeLayouItemModel = model; UpdateCaption(); } } void ObjectSpace_ObjectChanged(object sender, ObjectChangedEventArgs e) { if(e.PropertyName == nameof(Department)) { UpdateCaption(); } } private void View_CurrentObjectChanged(object sender, EventArgs e) { UpdateCaption(); } private void UpdateCaption() { if(officeLayouItemModel is not null) { var officeItem = View.FindItem($"{nameof(Department)}.{nameof(Department.Office)}"); var departmentItem = View.FindItem(nameof(Department)) as LookupPropertyEditor; officeLayouItemModel.Caption = $"Office ({departmentItem.PropertyValue})"; } } }

        Older Versions

        I modified your solution using JavaScript. I implemented a TestViewController that updates View captions when the View creates controls for the first time and calls a custom JavaScript function instead of Frame.SetView:

        C#
        using DevExpress.ExpressApp; using DevExpress.ExpressApp.Blazor; using DevExpress.ExpressApp.Blazor.Editors; using MainDemo.Module.BusinessObjects; using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; namespace MainDemo.Module.Blazor.Controllers { public class TestViewController : ObjectViewController<DetailView, Employee> { protected override void OnActivated() { base.OnActivated(); // Update a View captions for the first time; View.ObjectSpace.ObjectChanged += ObjectSpace_ObjectChanged; var departmentItem = View.FindItem(nameof(Department)) as LookupPropertyEditor; var officeItem = View.FindItem($"{nameof(Department)}.{nameof(Department.Office)}"); officeItem.Caption = $"{officeItem.Caption} ({departmentItem.PropertyValue})"; } // I changed your method by calling a native JavaScript method instead of calling Frame.SetView; void ObjectSpace_ObjectChanged(object sender, ObjectChangedEventArgs e) { var obj = View.CurrentObject as Employee; if(obj != null) { if(e.PropertyName == nameof(Department)) { var officeItem = View.FindItem($"{nameof(Department)}.{nameof(Department.Office)}"); if(officeItem != null) { var jsRuntime = ((BlazorApplication)Application).ServiceProvider.GetRequiredService<IJSRuntime>(); jsRuntime.InvokeVoidAsync("UpdateCaption", officeItem.Id.ToLower(), $"Office ({e.NewValue})"); } } } } override protected void OnDeactivated() { View.ObjectSpace.ObjectChanged -= ObjectSpace_ObjectChanged; } } }

        This is a custom JavaScript function that changes the caption's text by ViewItem's Id:

        JavaScript
        window.UpdateCaption = function(id, value) { document.querySelector(`label[class~="xaf-item-${id}"]`).innerHTML = value; // This selector is used to correctly process such class names: .xaf-item-department.office }

        I attached a video (MP4) to demonstrate how it works.

        I'd like to emphasize that it is an internal and undocumented API, which is designed for internal purposes and is subject to change without notice in future versions. So, please consider the risks before using such internal APIs. We hope to improve customization of XAF's Blazor LayoutComponent and to provide a pre-built documented solution for such issues. It's one of our priority tasks.

        Please let me know how this temporary solution I suggested works for you.

        Thanks,
        Stanislav

          Show previous comments (2)

            Considered done.

            Dennis Garavsky (DevExpress) 3 years ago

              Thank you, Kartal!

              A A
              Arkady (DevExpress) 2 years ago

                Hello,

                I am happy to inform you that the DxFormLayout component and its items are now accessible for customization since v22.2.5 and later. The BlazorLayoutManager.ItemCreated event is triggered when a layout item model is created. By handling this event, you can manage the properties of the following components: DxFormLayout, DxFormLayoutTabPages, DxFormLayoutTabPage, DxFormLayoutGroup, and DxFormLayoutItem.

                We also updated our example on XAF Blazor with a new approach. Please refer to the following links:

                Thanks,
                Arkady

                Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

                Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.