diff --git a/maui/src/Picker/PickerBase.cs b/maui/src/Picker/PickerBase.cs index 1a9437e..5b426a3 100644 --- a/maui/src/Picker/PickerBase.cs +++ b/maui/src/Picker/PickerBase.cs @@ -207,7 +207,7 @@ internal void InvokeCancelButtonClickedEvent(object sender, EventArgs eventArgs) /// Returns the internal selection or not internal bool IsScrollSelectionAllowed() { - if (Mode != PickerMode.Default && FooterView.Height != 0 && FooterView.ShowOkButton) + if (Mode != PickerMode.Default && (int)FooterView.Height != 0 && FooterView.ShowOkButton && FooterTemplate == null) { return true; } diff --git a/maui/src/Picker/SfDatePicker.cs b/maui/src/Picker/SfDatePicker.cs index 1faf9c3..ececd41 100644 --- a/maui/src/Picker/SfDatePicker.cs +++ b/maui/src/Picker/SfDatePicker.cs @@ -1411,6 +1411,22 @@ void InitializePickerStyle() SetDynamicResource(DisabledTextColorProperty, "SfDatePickerDisabledTextColor"); } + /// + /// Method to update Selected Date based on confirmation. + /// + /// Denotes whether selected value needs to be updated + void UpdateInternalValueToSelection(bool shouldUpdateSelection) + { + // If the picker is not in Default mode and an internal selected date exists + if (shouldUpdateSelection && _internalSelectedDate != null && !DatePickerHelper.IsSameDate(_internalSelectedDate, SelectedDate)) + { + // Update the selected date with the internal selected date + SelectedDate = _internalSelectedDate.Value; + // Clear the internal selected date after applying it + _internalSelectedDate = null; + } + } + #endregion #region Override Methods @@ -1533,19 +1549,7 @@ protected override void OnPopupOpened(EventArgs e) /// The event arguments protected override void OnOkButtonClicked(EventArgs e) { - // If the picker is not in Default mode and an internal selected date exists - if (IsScrollSelectionAllowed() && _internalSelectedDate != null) - { - // If the internal selected date is different from the currently selected date - if (!DatePickerHelper.IsSameDate(_internalSelectedDate, SelectedDate)) - { - // Update the selected date with the internal selected date - SelectedDate = _internalSelectedDate.Value; - // Clear the internal selected date after applying it - _internalSelectedDate = null; - } - } - + UpdateInternalValueToSelection(IsScrollSelectionAllowed()); InvokeOkButtonClickedEvent(this, e); if (AcceptCommand != null && AcceptCommand.CanExecute(e)) { diff --git a/maui/src/Picker/SfDateTimePicker.cs b/maui/src/Picker/SfDateTimePicker.cs index 6254833..9b078a6 100644 --- a/maui/src/Picker/SfDateTimePicker.cs +++ b/maui/src/Picker/SfDateTimePicker.cs @@ -1888,6 +1888,7 @@ void GenerateDatePickerColumns() List formatStringOrder = DatePickerHelper.GetFormatStringOrder(out dayFormat, out monthFormat, DateFormat); DateTime maxDate = DatePickerHelper.GetValidMaxDate(MinimumDate, MaximumDate); DateTime date = SelectedDate ?? _previousSelectedDateTime; + date = GetScrollSelectedDateTime(date); DateTime selectedDate = DatePickerHelper.GetValidDateTime(date, MinimumDate, maxDate); ObservableCollection pickerColumns = new ObservableCollection(); foreach (int index in formatStringOrder) @@ -2034,6 +2035,7 @@ void GenerateTimePickerColumns() List formatStringOrder = TimePickerHelper.GetFormatStringOrder(out hourFormat, TimeFormat); DateTime maxDate = DatePickerHelper.GetValidMaxDate(MinimumDate, MaximumDate); DateTime date = SelectedDate ?? _previousSelectedDateTime; + date = GetScrollSelectedDateTime(date); DateTime selectedDate = DatePickerHelper.GetValidDateTime(date, MinimumDate, maxDate); TimeSpan selectedTime = new TimeSpan(0, selectedDate.Hour, selectedDate.Minute, selectedDate.Second, selectedDate.Millisecond); ObservableCollection pickerColumns = new ObservableCollection(); @@ -2157,7 +2159,7 @@ PickerColumn GenerateMinuteColumn(TimeSpan? selectedTime, DateTime? selectedDate DateTime? minimumDate = null; DateTime? maximumDate = null; DateTime maxDate = DatePickerHelper.GetValidMaxDate(MinimumDate, MaximumDate); - DateTime? date = SelectedDate ?? _previousSelectedDateTime; + DateTime? date = selectedDate ?? _previousSelectedDateTime; if (date.Value.Date <= MinimumDate.Date) { minimumDate = MinimumDate; @@ -2320,6 +2322,35 @@ void IntializePickerStyle() SetDynamicResource(DisabledTextColorProperty, "SfDateTimePickerDisabledTextColor"); } + /// + /// Method to update Selected Date and Time based on confirmation. + /// + /// Denotes whether selected value needs to be updated + void UpdateInternalValueToSelection(bool shouldUpdateSelection) + { + // If the picker is not in Default mode and an internal selected date-time exists + if (shouldUpdateSelection && _internalSelectedDateTime != null && !DatePickerHelper.IsSameDateTime(_internalSelectedDateTime, SelectedDate)) + { + // Update the selected date with the internal selected date-time + SelectedDate = _internalSelectedDateTime.Value; + // Clear the internal selected date-time after applying it + _internalSelectedDateTime = null; + } + } + + /// + /// Gets the internally selected date and time when scroll selection is allowed. + /// + /// + /// The value if scroll selection is enabled and an internal selection exists; otherwise, the provided parameter. + /// + DateTime GetScrollSelectedDateTime(DateTime dateTime) + { + return IsScrollSelectionAllowed() && _internalSelectedDateTime.HasValue + ? _internalSelectedDateTime.Value + : dateTime; + } + #endregion #region Override Methods @@ -2498,6 +2529,19 @@ protected override void OnPickerLoading() /// The event arguments. protected override void OnPopupClosed(EventArgs e) { + if (_internalSelectedDateTime != null) + { + _internalSelectedDateTime = null; + if (_selectedIndex == 0) + { + BaseHeaderView.DateText = GetDateHeaderText(); + } + else + { + BaseHeaderView.TimeText = this.GetTimeHeaderText(); + } + } + InvokeClosedEvent(this, e); } @@ -2525,19 +2569,7 @@ protected override void OnPopupOpened(EventArgs e) /// The event arguments. protected override void OnOkButtonClicked(EventArgs e) { - // If the picker is not in Default mode and an internal selected date-time exists - if (IsScrollSelectionAllowed() && _internalSelectedDateTime != null) - { - // If the internal selected date-time is different from the currently selected date - if (!DatePickerHelper.IsSameDateTime(_internalSelectedDateTime, SelectedDate)) - { - // Update the selected date with the internal selected date-time - SelectedDate = _internalSelectedDateTime.Value; - // Clear the internal selected date-time after applying it - _internalSelectedDateTime = null; - } - } - + UpdateInternalValueToSelection(IsScrollSelectionAllowed()); InvokeOkButtonClickedEvent(this, e); if (AcceptCommand != null && AcceptCommand.CanExecute(e)) { diff --git a/maui/src/Picker/SfPicker.cs b/maui/src/Picker/SfPicker.cs index 77ef49f..67d4234 100644 --- a/maui/src/Picker/SfPicker.cs +++ b/maui/src/Picker/SfPicker.cs @@ -633,6 +633,36 @@ void InitializePickerStyle() SetDynamicResource(NormalFontSizeProperty, "SfPickerNormalFontSize"); } + /// + /// Method to update Selected Index based on confirmation. + /// + /// Denotes whether selected value needs to be updated + void UpdateInternalValueToSelection(bool shouldUpdateSelection) + { + // If the picker is not in Default mode and has a single column + if (shouldUpdateSelection && BaseColumns.Count == 1) + { + // Iterate through each column in the picker + foreach (PickerColumn column in this.Columns) + { + // Skip columns that have no internal selection + if (column._internalSelectedIndex == -1) + { + continue; + } + // If the column has a valid item source and it's a list + if (column.ItemsSource != null && column.ItemsSource is IList pickerCollection && column.SelectedIndex >= 0 && column.SelectedIndex < pickerCollection.Count) + { + // Apply the internal selected index to the actual selected index + int selectedIndex = column._internalSelectedIndex; + column._internalSelectedIndex = -1; + // Apply selection + column.SelectedIndex = selectedIndex; + } + } + } + } + #endregion #region Override Methods @@ -759,33 +789,7 @@ protected override void OnPopupOpened(EventArgs e) /// The event arguments. protected override void OnOkButtonClicked(EventArgs e) { - // If the picker is not in Default mode - if (IsScrollSelectionAllowed() && BaseColumns.Count == 1) - { - // Iterate through each column in the picker - foreach (PickerColumn column in this.Columns) - { - // Skip columns that have no internal selection - if (column._internalSelectedIndex == -1) - { - continue; - } - // If the column has a valid item source and it's a list - if (column.ItemsSource != null && column.ItemsSource is IList pickerCollection) - { - // Check if the selected index is within the valid range of the collection - if (column.SelectedIndex >= 0 && column.SelectedIndex < pickerCollection.Count) - { - // Apply the internal selected index to the actual selected index - int selectedIndex = column._internalSelectedIndex; - column._internalSelectedIndex = -1; - // Apply selection - column.SelectedIndex = selectedIndex; - } - } - } - } - + UpdateInternalValueToSelection(IsScrollSelectionAllowed()); InvokeOkButtonClickedEvent(this, e); if (AcceptCommand != null && AcceptCommand.CanExecute(e)) { diff --git a/maui/src/Picker/SfTimePicker.cs b/maui/src/Picker/SfTimePicker.cs index 1ea6ab1..5831daf 100644 --- a/maui/src/Picker/SfTimePicker.cs +++ b/maui/src/Picker/SfTimePicker.cs @@ -1533,6 +1533,22 @@ void InitializePickerStyle() SetDynamicResource(DisabledTextColorProperty, "SfTimePickerDisabledTextColor"); } + /// + /// Method to update Selected Time based on confirmation. + /// + /// Denotes whether selected value needs to be updated + void UpdateInternalValueToSelection(bool shouldUpdateSelection) + { + // If the picker is not in Default mode and an internal selected time exists + if (shouldUpdateSelection && _internalSelectedTime != null && !TimePickerHelper.IsSameTimeSpan(_internalSelectedTime, SelectedTime)) + { + // Update the selected time with the internal selected time + this.SelectedTime = _internalSelectedTime.Value; + // Clear the internal selected time after applying it + _internalSelectedTime = null; + } + } + #endregion #region Override Methods @@ -1655,19 +1671,7 @@ protected override void OnPopupOpened(EventArgs e) /// The event arguments protected override void OnOkButtonClicked(EventArgs e) { - // If the picker is not in Default mode and an internal selected time exists - if (IsScrollSelectionAllowed() && _internalSelectedTime != null) - { - // If the internal selected time is different from the currently selected time - if (!TimePickerHelper.IsSameTimeSpan(_internalSelectedTime, SelectedTime)) - { - // Update the selected time with the internal selected time - this.SelectedTime = _internalSelectedTime.Value; - // Clear the internal selected time after applying it - _internalSelectedTime = null; - } - } - + UpdateInternalValueToSelection(IsScrollSelectionAllowed()); InvokeOkButtonClickedEvent(this, e); if (AcceptCommand != null && AcceptCommand.CanExecute(e)) { diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DatePickerUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DatePickerUnitTest.cs index 1c0e6ca..53eb1b5 100644 --- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DatePickerUnitTest.cs +++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DatePickerUnitTest.cs @@ -3185,6 +3185,79 @@ public void DialogMode_DateBoundaryValues_ValidatesCorrectBehavior() Assert.True(selectionChangedFired, "SelectionChanged should fire for valid boundary values"); } + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenFooterTemplateIsSet_EvenIfOtherConditionsPass() + { + // Arrange: All other conditions met + var picker = new SfDatePicker + { + Mode = PickerMode.Dialog, + FooterTemplate = new DataTemplate(() => + { + return new Label { Text = "Footer Content" }; + }) + }; + picker.FooterView.Height = 24; // non-zero + picker.FooterView.ShowOkButton = true; // true + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.False(allowed, "Scroll selection must be disallowed when a custom FooterTemplate is set."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsTrue_WhenAllConditionsMet_AndFooterTemplateIsNull() + { + // Arrange: All conditions met and FooterTemplate is null + var picker = new SfDatePicker + { + Mode = PickerMode.Dialog, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = true; + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.True(allowed, "Scroll selection should be allowed when FooterTemplate is null and other conditions are met."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenModeIsDefault() + { + // Arrange: Mode is Default should disable regardless of other fields + var picker = new SfDatePicker + { + Mode = PickerMode.Default, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = true; + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.False(allowed, "Scroll selection should be disallowed when Mode == Default."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenShowOkButtonIsFalse() + { + var picker = new SfDatePicker + { + Mode = PickerMode.Dialog, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = false; + + var allowed = picker.IsScrollSelectionAllowed(); + + Assert.False(allowed, "Scroll selection should be disallowed when FooterView.ShowOkButton is false."); + } + #endregion } } diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DateTimePickerUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DateTimePickerUnitTest.cs index 5d77633..def4b7a 100644 --- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DateTimePickerUnitTest.cs +++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DateTimePickerUnitTest.cs @@ -1971,6 +1971,79 @@ public void DialogMode_DateTimeBoundaryValues_ValidatesCorrectBehavior() Assert.True(selectionChangedFired, "SelectionChanged should fire for valid boundary values"); } + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenFooterTemplateIsSet_EvenIfOtherConditionsPass() + { + // Arrange: All other conditions met + var picker = new SfDateTimePicker + { + Mode = PickerMode.Dialog, + FooterTemplate = new DataTemplate(() => + { + return new Label { Text = "Footer Content" }; + }) + }; + picker.FooterView.Height = 24; // non-zero + picker.FooterView.ShowOkButton = true; // true + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.False(allowed, "Scroll selection must be disallowed when a custom FooterTemplate is set."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsTrue_WhenAllConditionsMet_AndFooterTemplateIsNull() + { + // Arrange: All conditions met and FooterTemplate is null + var picker = new SfDateTimePicker + { + Mode = PickerMode.Dialog, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = true; + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.True(allowed, "Scroll selection should be allowed when FooterTemplate is null and other conditions are met."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenModeIsDefault() + { + // Arrange: Mode is Default should disable regardless of other fields + var picker = new SfDateTimePicker + { + Mode = PickerMode.Default, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = true; + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.False(allowed, "Scroll selection should be disallowed when Mode == Default."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenShowOkButtonIsFalse() + { + var picker = new SfDateTimePicker + { + Mode = PickerMode.Dialog, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = false; + + var allowed = picker.IsScrollSelectionAllowed(); + + Assert.False(allowed, "Scroll selection should be disallowed when FooterView.ShowOkButton is false."); + } + #endregion } } diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerUnitTest.cs index 509906d..04cef8b 100644 --- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerUnitTest.cs +++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerUnitTest.cs @@ -2522,6 +2522,79 @@ public void DefaultMode_SelectionCommittedImmediately_ValidatesCorrectBehavior() Assert.True(selectionChangedFired, "SelectionChanged should fire immediately in Default mode"); } + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenFooterTemplateIsSet_EvenIfOtherConditionsPass() + { + // Arrange: All other conditions met + var picker = new SfPicker + { + Mode = PickerMode.Dialog, + FooterTemplate = new DataTemplate(() => + { + return new Label { Text = "Footer Content" }; + }) + }; + picker.FooterView.Height = 24; // non-zero + picker.FooterView.ShowOkButton = true; // true + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.False(allowed, "Scroll selection must be disallowed when a custom FooterTemplate is set."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsTrue_WhenAllConditionsMet_AndFooterTemplateIsNull() + { + // Arrange: All conditions met and FooterTemplate is null + var picker = new SfPicker + { + Mode = PickerMode.Dialog, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = true; + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.True(allowed, "Scroll selection should be allowed when FooterTemplate is null and other conditions are met."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenModeIsDefault() + { + // Arrange: Mode is Default should disable regardless of other fields + var picker = new SfPicker + { + Mode = PickerMode.Default, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = true; + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.False(allowed, "Scroll selection should be disallowed when Mode == Default."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenShowOkButtonIsFalse() + { + var picker = new SfPicker + { + Mode = PickerMode.Dialog, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = false; + + var allowed = picker.IsScrollSelectionAllowed(); + + Assert.False(allowed, "Scroll selection should be disallowed when FooterView.ShowOkButton is false."); + } + #endregion } } diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerUnitTest.cs index edfadef..aa9a1e0 100644 --- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerUnitTest.cs +++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerUnitTest.cs @@ -3069,6 +3069,79 @@ public void DialogMode_TimeBoundaryValues_ValidatesCorrectBehavior() Assert.True(selectionChangedFired, "SelectionChanged should fire for valid boundary values"); } + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenFooterTemplateIsSet_EvenIfOtherConditionsPass() + { + // Arrange: All other conditions met + var picker = new SfTimePicker + { + Mode = PickerMode.Dialog, + FooterTemplate = new DataTemplate(() => + { + return new Label { Text = "Footer Content" }; + }) + }; + picker.FooterView.Height = 24; // non-zero + picker.FooterView.ShowOkButton = true; // true + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.False(allowed, "Scroll selection must be disallowed when a custom FooterTemplate is set."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsTrue_WhenAllConditionsMet_AndFooterTemplateIsNull() + { + // Arrange: All conditions met and FooterTemplate is null + var picker = new SfTimePicker + { + Mode = PickerMode.Dialog, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = true; + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.True(allowed, "Scroll selection should be allowed when FooterTemplate is null and other conditions are met."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenModeIsDefault() + { + // Arrange: Mode is Default should disable regardless of other fields + var picker = new SfTimePicker + { + Mode = PickerMode.Default, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = true; + + // Act + var allowed = picker.IsScrollSelectionAllowed(); + + // Assert + Assert.False(allowed, "Scroll selection should be disallowed when Mode == Default."); + } + + [Fact] + public void IsScrollSelectionAllowed_ReturnsFalse_WhenShowOkButtonIsFalse() + { + var picker = new SfTimePicker + { + Mode = PickerMode.Dialog, + }; + picker.FooterView.Height = 24; + picker.FooterView.ShowOkButton = false; + + var allowed = picker.IsScrollSelectionAllowed(); + + Assert.False(allowed, "Scroll selection should be disallowed when FooterView.ShowOkButton is false."); + } + #endregion } }