每当我在 iOS 上键入内容时,ListView 中的 Xamarin Forms 编辑器都会失去焦点
Xamarin Forms editors in ListViews are losing focus whenever I type something on iOS
我在 Xamarin Forms 中有一个 ListView,其中包含带有编辑器的自定义 ViewCell。我有一个函数订阅了编辑器的 TextChanged 事件,该事件检查文本中是否有换行符,如果找到,它会删除它们并使编辑器失去焦点。它在 Android 上正常工作,但在 iOS 上,每当我键入 任何内容 时,编辑器都会失去焦点,而不仅仅是换行符。我该如何解决这个问题?
XAML 页面:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
mc:Ignorable="d"
x:Class="Partylist.Views.ChecklistPage"
ios:Page.UseSafeArea="True">
...
<ContentPage.Content>
<!--Main layout of the page-->
<StackLayout>
<!--ListView of the checklist items-->
<ListView x:Name="ChecklistView"
HeightRequest="300"
HasUnevenRows="true"
ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<SwipeView>
<!--Swipe from the right to make some options
appear-->
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Invoked="OnDelete"
CommandParameter="{Binding .}"
Text="Delete"
BackgroundColor="#ff418b"
IsDestructive="true"/>
</SwipeItems>
</SwipeView.RightItems>
<!--This is the actual content-->
<StackLayout Orientation="Horizontal"
Padding="20,5"
VerticalOptions="FillAndExpand">
<ContentView Content="{Binding ItemCheckbox}"/>
<ContentView Content="{Binding ItemEditor}"
VerticalOptions="FillAndExpand"/>
</StackLayout>
</SwipeView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
...
</StackLayout>
</ContentPage.Content>
</ContentPage>
代码:
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Partylist.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ChecklistPage : ContentPage
{
// Struct for items on the checklist.
private struct Item
{
public Editor ItemEditor { get; set; }
public CheckBox ItemCheckbox { get; set; }
}
// Create a list of contact structs to populate the ListView.
ObservableCollection<Item> items;
// Flag for when an item is added to the list.
bool itemAdded = false;
...
// Override for OnAppearing().
protected override void OnAppearing()
{
// Makes the page appear.
base.OnAppearing();
// Set the page's title to be the name of the selected list.
Title = App.selectedList.Name;
// Make a toolbar item appear to access the Main Checklist
// unless we are already there.
if (App.selectedList.ListFile.Name.EndsWith(".mchec"))
{
ToolbarItems.Remove(MainChecklistButton);
}
// Set the binding context of the page to itself.
BindingContext = this;
// Start the timer for the tips banner if it is stopped.
App.tipTimer.Start();
// Set the banner's text to the current tip's sumamry.
tipLabel.Text = ((App)App.Current).CurrentTip.Summary;
OnPropertyChanged("CurrentTip");
// Subscribe the OnTipUpdate function to the tipUpdate event in the app
// class.
App.TipUpdate += OnTipUpdate;
// Make the ObservableCOllection reference something.
items = new ObservableCollection<Item>();
// Open a stream to the list that we want to display.
using (StreamReader listReader = new StreamReader(App.selectedList
.ListFile.FullName))
{
// Loop through the file and read data into the list.
while (!listReader.EndOfStream)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new Editor()
{
Text = listReader.ReadLine(),
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 300
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = bool.Parse(listReader.ReadLine())
}
};
// Subscribe OnTextChanged() to the new item's editor's
// TextChanged event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new item to the list.
items.Add(newItem);
// Make the ListView update.
OnPropertyChanged("contacts");
}
// Once everything is loaded, close the file.
listReader.Close();
ChecklistView.ItemsSource = items;
}
}
...
// Function for when the "Add New Contact" button is clicked.
private void OnAddNewItemClicked(object sender, EventArgs e)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new Editor()
{
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 300
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = false
}
};
// Subscribe OnTextChanged() to the new item's editor's
// TextChanged event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new contact to the list.
items.Add(newItem);
// Set the "itemAdded" flag to true.
itemAdded = true;
// Make the ListView update.
ChecklistView.ItemsSource = items;
OnPropertyChanged("items");
// Select the new item so it can be focused.
ChecklistView.SelectedItem = items.ElementAt(items.Count - 1);
}
// Function for when an item is selected, used to set the focus to
// a newly added item in the list.
private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
// Only runs this if an item was added (as opposed to being
// read in from the file).
if (itemAdded)
{
if (e.SelectedItem == null) return;
await Task.Delay(100); // Change the delay time if Focus() doesn't work.
((Item)e.SelectedItem).ItemEditor.Focus();
ChecklistView.SelectedItem = null;
itemAdded = false;
}
}
// Function for when the text of an editor is changed.
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
// If the editor's text now contains a newline...
if (((Editor)sender).Text.Contains("\n"))
{
// Split the string at the newline and recombine it. This
// will get rid of the newline.
string[] tempArray = ((Editor)sender).Text.Split('\n');
string tempStr = "";
foreach (string str in tempArray)
{
tempStr += str;
}
((Editor)sender).Text = tempStr;
// Unfocus the editor.
((Editor)sender).Unfocus();
}
// Force the cell's size to update (it is the parent of the
// parent of the parent of the parent of the editor).
((ViewCell)((Editor)sender).Parent.Parent.Parent.Parent).ForceUpdateSize();
}
...
}
}
调用ForceUpdateSize
将导致编辑器失去焦点。您可以将 ForceUpdateSize
放入 Task.Run
方法并在 ForceUpdateSize();
之后调用焦点。
private async void OnTextChanged(object sender, TextChangedEventArgs e)
{
//...
await Task.Run(()=>{
((ViewCell)((Editor)sender).Parent.Parent.Parent.Parent).ForceUpdateSize();
});
((Editor)sender).Focus();
}
我用 CollectionView 替换了 ListView,这似乎解决了问题。
此外,CollectionView 没有 HasUnevenRows 属性,因此必须删除它并且它不使用单元格,因此也必须删除 ForceUpdateSize() 方法。
我在 Xamarin Forms 中有一个 ListView,其中包含带有编辑器的自定义 ViewCell。我有一个函数订阅了编辑器的 TextChanged 事件,该事件检查文本中是否有换行符,如果找到,它会删除它们并使编辑器失去焦点。它在 Android 上正常工作,但在 iOS 上,每当我键入 任何内容 时,编辑器都会失去焦点,而不仅仅是换行符。我该如何解决这个问题?
XAML 页面:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
mc:Ignorable="d"
x:Class="Partylist.Views.ChecklistPage"
ios:Page.UseSafeArea="True">
...
<ContentPage.Content>
<!--Main layout of the page-->
<StackLayout>
<!--ListView of the checklist items-->
<ListView x:Name="ChecklistView"
HeightRequest="300"
HasUnevenRows="true"
ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<SwipeView>
<!--Swipe from the right to make some options
appear-->
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Invoked="OnDelete"
CommandParameter="{Binding .}"
Text="Delete"
BackgroundColor="#ff418b"
IsDestructive="true"/>
</SwipeItems>
</SwipeView.RightItems>
<!--This is the actual content-->
<StackLayout Orientation="Horizontal"
Padding="20,5"
VerticalOptions="FillAndExpand">
<ContentView Content="{Binding ItemCheckbox}"/>
<ContentView Content="{Binding ItemEditor}"
VerticalOptions="FillAndExpand"/>
</StackLayout>
</SwipeView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
...
</StackLayout>
</ContentPage.Content>
</ContentPage>
代码:
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Partylist.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ChecklistPage : ContentPage
{
// Struct for items on the checklist.
private struct Item
{
public Editor ItemEditor { get; set; }
public CheckBox ItemCheckbox { get; set; }
}
// Create a list of contact structs to populate the ListView.
ObservableCollection<Item> items;
// Flag for when an item is added to the list.
bool itemAdded = false;
...
// Override for OnAppearing().
protected override void OnAppearing()
{
// Makes the page appear.
base.OnAppearing();
// Set the page's title to be the name of the selected list.
Title = App.selectedList.Name;
// Make a toolbar item appear to access the Main Checklist
// unless we are already there.
if (App.selectedList.ListFile.Name.EndsWith(".mchec"))
{
ToolbarItems.Remove(MainChecklistButton);
}
// Set the binding context of the page to itself.
BindingContext = this;
// Start the timer for the tips banner if it is stopped.
App.tipTimer.Start();
// Set the banner's text to the current tip's sumamry.
tipLabel.Text = ((App)App.Current).CurrentTip.Summary;
OnPropertyChanged("CurrentTip");
// Subscribe the OnTipUpdate function to the tipUpdate event in the app
// class.
App.TipUpdate += OnTipUpdate;
// Make the ObservableCOllection reference something.
items = new ObservableCollection<Item>();
// Open a stream to the list that we want to display.
using (StreamReader listReader = new StreamReader(App.selectedList
.ListFile.FullName))
{
// Loop through the file and read data into the list.
while (!listReader.EndOfStream)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new Editor()
{
Text = listReader.ReadLine(),
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 300
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = bool.Parse(listReader.ReadLine())
}
};
// Subscribe OnTextChanged() to the new item's editor's
// TextChanged event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new item to the list.
items.Add(newItem);
// Make the ListView update.
OnPropertyChanged("contacts");
}
// Once everything is loaded, close the file.
listReader.Close();
ChecklistView.ItemsSource = items;
}
}
...
// Function for when the "Add New Contact" button is clicked.
private void OnAddNewItemClicked(object sender, EventArgs e)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new Editor()
{
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 300
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = false
}
};
// Subscribe OnTextChanged() to the new item's editor's
// TextChanged event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new contact to the list.
items.Add(newItem);
// Set the "itemAdded" flag to true.
itemAdded = true;
// Make the ListView update.
ChecklistView.ItemsSource = items;
OnPropertyChanged("items");
// Select the new item so it can be focused.
ChecklistView.SelectedItem = items.ElementAt(items.Count - 1);
}
// Function for when an item is selected, used to set the focus to
// a newly added item in the list.
private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
// Only runs this if an item was added (as opposed to being
// read in from the file).
if (itemAdded)
{
if (e.SelectedItem == null) return;
await Task.Delay(100); // Change the delay time if Focus() doesn't work.
((Item)e.SelectedItem).ItemEditor.Focus();
ChecklistView.SelectedItem = null;
itemAdded = false;
}
}
// Function for when the text of an editor is changed.
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
// If the editor's text now contains a newline...
if (((Editor)sender).Text.Contains("\n"))
{
// Split the string at the newline and recombine it. This
// will get rid of the newline.
string[] tempArray = ((Editor)sender).Text.Split('\n');
string tempStr = "";
foreach (string str in tempArray)
{
tempStr += str;
}
((Editor)sender).Text = tempStr;
// Unfocus the editor.
((Editor)sender).Unfocus();
}
// Force the cell's size to update (it is the parent of the
// parent of the parent of the parent of the editor).
((ViewCell)((Editor)sender).Parent.Parent.Parent.Parent).ForceUpdateSize();
}
...
}
}
调用ForceUpdateSize
将导致编辑器失去焦点。您可以将 ForceUpdateSize
放入 Task.Run
方法并在 ForceUpdateSize();
之后调用焦点。
private async void OnTextChanged(object sender, TextChangedEventArgs e)
{
//...
await Task.Run(()=>{
((ViewCell)((Editor)sender).Parent.Parent.Parent.Parent).ForceUpdateSize();
});
((Editor)sender).Focus();
}
我用 CollectionView 替换了 ListView,这似乎解决了问题。
此外,CollectionView 没有 HasUnevenRows 属性,因此必须删除它并且它不使用单元格,因此也必须删除 ForceUpdateSize() 方法。