Repository: AvaloniaUI/Avalonia

Issue:
CustomDateFormatString is not used for text input in CalendarDatePicker
### Describe the bug

The CalendarDatePicker with a CustomDateFormatString seems to display the date correctly but ignores the custom date format when the date is entered by keyboard.

### To Reproduce

1. Create a CalendarDatePicker with CustomDateFormatString set to "dd.MM.yyyy"
`<CalendarDatePicker SelectedDateFormat="Custom" CustomDateFormatString="dd.MM.yyyy" SelectedDate="{Binding Datum}">`
2. Type in the date "17.10.2024" and press enter => not accepted,  the date changes to previous value 
3. Type in the date "12.10.2024" and press enter => the date changes to 10.12.2024

### Expected behavior

Type in the date "17.10.2024" and press enter => "17.10.2024" ramains in the textbox
Type in the date "12.10.2024" and press enter => "12.10.2024" ramains in the textbox

### Avalonia version

11.1.0

### OS

_No response_

### Additional context

I'm new to avalonia, but looking at the code the problem seems to be in CalendarDatePicker.cs `ParseText(string text) `  in Line 733:
` newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat());`
Shouldn't CustomDateFormatString be used here when SelectedDateFormat is set to Custom?


Hints:
@JokaDev without looking too deep into, I think you may be right. Maybe you can file a PR to fix it? Adding some tests if possible would be great. 

Patch:
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
index ec54533f4e1..eaf632039b8 100644
--- a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
@@ -730,7 +730,9 @@ private void OpenDropDown()
             // the TextParseError event
             try
             {
-                newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat());
+                newSelectedDate = SelectedDateFormat == CalendarDatePickerFormat.Custom && !string.IsNullOrEmpty(CustomDateFormatString) ?
+                    DateTime.ParseExact(text, CustomDateFormatString, DateTimeHelper.GetCurrentDateFormat()) :
+                    DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat());
 
                 if (Calendar.IsValidDateSelection(this._calendar!, newSelectedDate))
                 {
@@ -899,6 +901,11 @@ private void SetWaterMarkText()
 
                     switch (SelectedDateFormat)
                     {
+                        case CalendarDatePickerFormat.Custom:
+                            {
+                                watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, CustomDateFormatString);
+                                break;
+                            }
                         case CalendarDatePickerFormat.Long:
                             {
                                 watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.LongDatePattern.ToString());
@@ -911,6 +918,7 @@ private void SetWaterMarkText()
                                 break;
                             }
                     }
+
                     _textBox.Watermark = watermarkText;
                 }
                 else


Test patch:
diff --git a/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs b/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs
index c3acf31caa7..013369f102a 100644
--- a/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs
@@ -1,17 +1,13 @@
-﻿using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
+using System;
 using System.Linq;
 using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Templates;
-using Avalonia.Data;
-using Avalonia.Markup.Data;
+using Avalonia.Input;
 using Avalonia.Platform;
 using Avalonia.UnitTests;
 using Moq;
 using Xunit;
+using System.Globalization;
 
 namespace Avalonia.Controls.UnitTests
 {
@@ -73,6 +69,34 @@ public void Adding_Blackout_Dates_Containing_Selected_Date_Should_Throw()
             }
         }
 
+        [Fact]
+        public void Setting_Date_Manually_With_CustomDateFormatString_Should_Be_Accepted()
+        {
+            CultureInfo.CurrentCulture = CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
+            using (UnitTestApplication.Start(Services))
+            {
+                CalendarDatePicker datePicker = CreateControl();
+                datePicker.SelectedDateFormat = CalendarDatePickerFormat.Custom;
+                datePicker.CustomDateFormatString = "dd.MM.yyyy";
+
+                var tb = GetTextBox(datePicker);
+
+                tb.Clear();
+                RaiseTextEvent(tb, "17.10.2024");
+                RaiseKeyEvent(tb, Key.Enter, KeyModifiers.None);
+
+                Assert.Equal("17.10.2024", datePicker.Text);
+                Assert.True(CompareDates(datePicker.SelectedDate.Value, new DateTime(2024, 10, 17)));
+
+                tb.Clear();
+                RaiseTextEvent(tb, "12.10.2024");
+                RaiseKeyEvent(tb, Key.Enter, KeyModifiers.None);
+
+                Assert.Equal("12.10.2024", datePicker.Text);
+                Assert.True(CompareDates(datePicker.SelectedDate.Value, new DateTime(2024, 10, 12)));
+            }
+        }
+
         private static TestServices Services => TestServices.MockThreadingInterface.With(
             standardCursorFactory: Mock.Of<ICursorFactory>());
 
@@ -127,5 +151,32 @@ private static IControlTemplate CreateTemplate()
             });
 
         }
+
+        private TextBox GetTextBox(CalendarDatePicker control)
+        {
+            return control.GetTemplateChildren()
+                .OfType<TextBox>()
+                .First();
+        }
+
+        private static void RaiseKeyEvent(TextBox textBox, Key key, KeyModifiers inputModifiers)
+        {
+            textBox.RaiseEvent(new KeyEventArgs
+            {
+                RoutedEvent = InputElement.KeyDownEvent,
+                KeyModifiers = inputModifiers,
+                Key = key
+            });
+        }
+
+        private static void RaiseTextEvent(TextBox textBox, string text)
+        {
+            textBox.RaiseEvent(new TextInputEventArgs
+            {
+                RoutedEvent = InputElement.TextInputEvent,
+                Text = text
+            });
+        }
+
     }
 }