سؤال

في تطبيق WPF, لدي عدد من databound مربعات النص.على UpdateSourceTrigger هذه الارتباطات هي LostFocus.حفظ الكائن باستخدام القائمة "ملف".المشكلة لدي هو أنه من الممكن أن أدخل قيمة جديدة إلى النص ، حدد حفظ من القائمة ملف ثم لم تستمر القيمة الجديدة (واحد مرئية في مربع النص) لأن الوصول إلى القائمة لا إزالة التركيز من مربع النص.كيف يمكنني إصلاح هذا ؟ هل هناك طريقة لإجبار جميع عناصر التحكم في صفحة databind?

@palehorse:نقطة جيدة.للأسف, أنا بحاجة إلى استخدام LostFocus كما UpdateSourceTrigger من أجل دعم نوع من التحقق من صحة أريد.

@dmo:لقد فكرت في ذلك.بيد أنه يبدو وكأنه حقا غير لائق الحل بسيط نسبيا المشكلة.كما أنه يتطلب أن يكون هناك بعض السيطرة على الصفحة الذي هو دائما مرئية على التركيز.طلبي هو كلفه ، ومع ذلك ، لا يوجد مثل هذا التحكم بسهولة يطرح نفسه.

@Nidonocu:حقيقة أن استخدام القائمة لم نقل التركيز من مربع نص الخلط لي كذلك.لكن هذا السلوك أرى.التالي مثال بسيط يوضح مشكلتي:

<Window x:Class="WpfApplication2.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <ObjectDataProvider x:Key="MyItemProvider" />
    </Window.Resources>
    <DockPanel LastChildFill="True">
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Header="Save" Click="MenuItem_Click" />
            </MenuItem>
        </Menu>
        <StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}">
            <Label Content="Enter some text and then File > Save:" />
            <TextBox Text="{Binding ValueA}" />
            <TextBox Text="{Binding ValueB}" />
        </StackPanel>
    </DockPanel>
</Window>
using System;
using System.Text;
using System.Windows;
using System.Windows.Data;

namespace WpfApplication2
{
    public partial class Window1 : Window
    {
        public MyItem Item
        {
            get { return (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance as MyItem; }
            set { (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance = value; }
        }

        public Window1()
        {
            InitializeComponent();
            Item = new MyItem();
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(string.Format("At the time of saving, the values in the TextBoxes are:\n'{0}'\nand\n'{1}'", Item.ValueA, Item.ValueB));
        }
    }

    public class MyItem
    {
        public string ValueA { get; set; }
        public string ValueB { get; set; }
    }
}
هل كانت مفيدة؟

المحلول

افترض أن لديك مربع نص في نافذة الأدوات مع زر حفظ في ذلك.نفترض مربع نص نص الملكية لا بد أن عقار على الكائن الأعمال و الربط هو UpdateSourceTrigger يتم تعيين الخاصية إلى قيمة افتراضية LostFocus ، وهذا يعني أن لا بد القيمة دفعها مرة أخرى إلى الأعمال الكائن الخاصية عند النص يفقد التركيز الإدخال.كما نفترض أن شريط الأدوات زر حفظ لها الأوامر الملكية تعيين ApplicationCommands.الأمر حفظ.

في هذه الحالة إذا قمت بتحرير مربع النص وانقر على زر حفظ مع الماوس ، هناك مشكلة.عند النقر على زر في شريط الأدوات مربع نص لا تفقد التركيز.لأن النص هو الحدث LostFocus لا النار ، النص المنشأة ملزمة لا يتم تحديث المصدر خاصية الكائن الأعمال.

من الواضح يجب أن لا صحة وحفظ كائن لو كان آخرها تحرير قيمة في واجهة المستخدم لم يتم دفعت إلى الكائن.هذا هو بالضبط المشكلة كارل عملت حولها ، قبل كتابة التعليمات البرمجية في إطار يدويا بحثت عن النص مع التركيز على تحديث مصدر البيانات ملزمة.حل له عملت بشكل جيد, ولكن حصلت لي التفكير في حل العامة التي سيكون من المفيد أيضا خارج هذا السيناريو خاصة.أدخل CommandGroup...

مأخوذة من جوش سميث CodeProject المادة عن CommandGroup

نصائح أخرى

وجدت أن إزالة عناصر القائمة التي هي نطاق يتوقف من FocusScope من القائمة لأسباب مربع نص إلى فقدان التركيز بشكل صحيح.أنا لا أعتقد أن هذا ينطبق على جميع البنود المدرجة في القائمة ، ولكن بالتأكيد على حفظ أو التحقق من صحة العمل.

<Menu FocusManager.IsFocusScope="False" >

على افتراض أن هناك أكثر من عنصر تحكم واحد في علامة التبويب تسلسل الحل التالي يبدو أن العام الكامل (فقط قص ولصق)...

Control currentControl = System.Windows.Input.Keyboard.FocusedElement as Control;

if (currentControl != null)
{
    // Force focus away from the current control to update its binding source.
    currentControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
    currentControl.Focus();
}

هذا هو قبيح الإختراق ولكن ينبغي أيضا العمل

TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
if (focusedTextBox != null)
{
    focusedTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

هذا الرمز يتحقق إذا كان مربع نص التركيز...إذا تم العثور على 1...تحديث الملزمة المصدر!

الحل بسيط هو تحديث Xaml كود كما هو موضح أدناه

    <StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}"> 
        <Label Content="Enter some text and then File > Save:" /> 
        <TextBox Text="{Binding ValueA, UpdateSourceTrigger=PropertyChanged}" /> 
        <TextBox Text="{Binding ValueB, UpdateSourceTrigger=PropertyChanged}" /> 
    </StackPanel> 

هل حاولت وضع UpdateSourceTrigger إلى PropertyChanged?بدلا من ذلك, يمكنك استدعاء UpdateSOurce (طريقة) ، ولكن هذا يبدو مبالغة بعض الشيء و الهزائم الغرض من TwoWay ربط البيانات.

لقد واجهت هذه المشكلة و أفضل حل وجدته هو تغيير قابل للتركيز قيمة زر (أو أي مكون آخر مثل MenuItem) صحيح:

<Button Focusable="True" Command="{Binding CustomSaveCommand}"/>

والسبب في أنه يعمل, لأنه يفرض على زر للحصول على التركيز قبل أن يستدعي الأمر وبالتالي يجعل النص أو أي UIElement في هذا الشأن أن تفقد التركيز و رفع فقدت التركيز الحدث الذي يستدعي الربط إلى تغيير.

في حال كنت تستخدم يحدها الأوامر (كما كان لافتا في بلدي على سبيل المثال) ، جون سميث الحل لن تناسب بشكل جيد للغاية منذ لا يمكنك ربط StaticExtension في يحدها الملكية (ولا DP).

يمكنك تعيين التركيز إلى مكان آخر قبل الإنقاذ ؟

يمكنك القيام بذلك عن طريق استدعاء التركيز() على عنصر واجهة المستخدم.

يمكنك التركيز على أي عنصر تحتج على "حفظ".إذا كان المشغل الخاص بك هو LostFocus ثم لديك لنقل التركيز في مكان ما.حفظ لديه ميزة أنه لا تعديل و المنطقي أن المستخدم.

في هذا البحث إلى الإجابة عليه, أنا مشوشة قليلا أن سلوك ترونه يحدث بالتأكيد القانون من النقر فوق القائمة " ملف " أو ما يجب أن unfocus مربع نص ووضعه على القائمة ؟

أسهل طريقة هي تعيين التركيز في مكان ما.
يمكنك تعيين التركيز مرة أخرى على الفور ، ولكن تعيين التركيز في أي مكان سوف يؤدي LostFocus-الحدث أي نوع من السيطرة وجعلها تحديث الاشياء:

IInputElement x = System.Windows.Input.Keyboard.FocusedElement;
DummyField.Focus();
x.Focus();

طريقة أخرى للحصول على تركيز عنصر على عنصر الربط من تركز عنصر على الزناد التحديث يدويا.مثال على مربع نص " و " تحرير وسرد (كنت في حاجة لإضافة أي نوع التحكم تحتاج إلى الدعم):

TextBox t = Keyboard.FocusedElement as TextBox;
if ((t != null) && (t.GetBindingExpression(TextBox.TextProperty) != null))
  t.GetBindingExpression(TextBox.TextProperty).UpdateSource();

ComboBox c = Keyboard.FocusedElement as ComboBox;
if ((c != null) && (c.GetBindingExpression(ComboBox.TextProperty) != null))
  c.GetBindingExpression(ComboBox.TextProperty).UpdateSource();

ما رأيك في هذا ؟ أعتقد أنني وجدت طريقة لجعله أكثر قليلا من عام باستخدام التفكير.أنا حقا لا أحب فكرة الحفاظ على قائمة مثل بعض الأمثلة الأخرى.

var currentControl = System.Windows.Input.Keyboard.FocusedElement;
if (currentControl != null)
{
    Type type = currentControl.GetType();
    if (type.GetMethod("MoveFocus") != null && type.GetMethod("Focus") != null)
    {
        try
        {
            type.GetMethod("MoveFocus").Invoke(currentControl, new object[] { new TraversalRequest(FocusNavigationDirection.Next) });
            type.GetMethod("Focus").Invoke(currentControl, null);
        }
        catch (Exception ex)
        {
            throw new Exception("Unable to handle unknown type: " + type.Name, ex);
        }
    }
}

ترى أي مشاكل مع هذا ؟

حيث لاحظت هذه المسألة لا يزال الألم في المؤخرة إلى حل على عامة جدا طريقة, حاولت حلول مختلفة.

في نهاية المطاف واحد التي عملت بالنسبة لي:كلما كان هناك ضرورة أن التغييرات واجهة المستخدم يجب المصادقة على تحديث إلى مصادرها (التحقق من التغييرات على closeing نافذة ، وأداء حفظ العمليات،...) ، أدعو التحقق من صحة وظيفة الذي يفعل أشياء مختلفة:- تأكد من تركيز عنصر (مثل مربع نص مربع تحرير وسرد, ...) يفقد التركيز التي تؤدي الافتراضي updatesource السلوك - التحقق من صحة أي ضوابط داخل شجرة DependencyObject والتي تعطى وظيفة التحقق من صحة - تعيين التركيز العودة إلى الأصل تركز عنصر

وظيفة نفسها ترجع صحيح إذا كان كل شيء في النظام (التثبيت غير ناجحة) -> العمل الأصلي الخاص بك (closeing مع اختياري يسأل تأكيد ، saveing, ...) يمكن أن يستمر.وإلا الدالة return false و العمل الخاص بك لا يمكن أن يستمر لأن هناك أخطاء التحقق من الصحة على واحد أو أكثر من العناصر (مع مساعدة من عام ErrorTemplate على العناصر).

رمز (التحقق من صحة وظيفة استنادا إلى المادة الكشف عن WPF أخطاء التحقق من الصحة):

public static class Validator
{
    private static Dictionary<String, List<DependencyProperty>> gdicCachedDependencyProperties = new Dictionary<String, List<DependencyProperty>>();

    public static Boolean IsValid(DependencyObject Parent)
    {
        // Move focus and reset it to update bindings which or otherwise not processed until losefocus
        IInputElement lfocusedElement = Keyboard.FocusedElement;
        if (lfocusedElement != null && lfocusedElement is UIElement)
        {
            // Move to previous AND to next InputElement (if your next InputElement is a menu, focus will not be lost -> therefor move in both directions)
            (lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
            (lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
            Keyboard.ClearFocus();
        }

        if (Parent as UIElement == null || (Parent as UIElement).Visibility != Visibility.Visible)
            return true;

        // Validate all the bindings on the parent 
        Boolean lblnIsValid = true;
        foreach (DependencyProperty aDependencyProperty in GetAllDependencyProperties(Parent))
        {
            if (BindingOperations.IsDataBound(Parent, aDependencyProperty))
            {
                // Get the binding expression base. This way all kinds of bindings (MultiBinding, PropertyBinding, ...) can be updated
                BindingExpressionBase lbindingExpressionBase = BindingOperations.GetBindingExpressionBase(Parent, aDependencyProperty);
                if (lbindingExpressionBase != null)
                {
                    lbindingExpressionBase.ValidateWithoutUpdate();
                    if (lbindingExpressionBase.HasError)
                        lblnIsValid = false;
                }
            }
        }

        if (Parent is Visual || Parent is Visual3D)
        {
            // Fetch the visual children (in case of templated content, the LogicalTreeHelper will return no childs)
            Int32 lintVisualChildCount = VisualTreeHelper.GetChildrenCount(Parent);
            for (Int32 lintVisualChildIndex = 0; lintVisualChildIndex < lintVisualChildCount; lintVisualChildIndex++)
                if (!IsValid(VisualTreeHelper.GetChild(Parent, lintVisualChildIndex)))
                    lblnIsValid = false;
        }

        if (lfocusedElement != null)
            lfocusedElement.Focus();

        return lblnIsValid;
    }

    public static List<DependencyProperty> GetAllDependencyProperties(DependencyObject DependencyObject)
    {
        Type ltype = DependencyObject.GetType();
        if (gdicCachedDependencyProperties.ContainsKey(ltype.FullName))
            return gdicCachedDependencyProperties[ltype.FullName];

        List<DependencyProperty> llstDependencyProperties = new List<DependencyProperty>();
        List<FieldInfo> llstFieldInfos = ltype.GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static).Where(Field => Field.FieldType == typeof(DependencyProperty)).ToList();
        foreach (FieldInfo aFieldInfo in llstFieldInfos)
            llstDependencyProperties.Add(aFieldInfo.GetValue(null) as DependencyProperty);
        gdicCachedDependencyProperties.Add(ltype.FullName, llstDependencyProperties);

        return llstDependencyProperties;
    }
}

أنا باستخدام BindingGroup.

XAML:

<R:RibbonWindow Closing="RibbonWindow_Closing" ...>

    <FrameworkElement.BindingGroup>
        <BindingGroup />
    </FrameworkElement.BindingGroup>

    ...
</R:RibbonWindow>

C#

private void RibbonWindow_Closing(object sender, CancelEventArgs e) {
    e.Cancel = !NeedSave();
}

bool NeedSave() {
    BindingGroup.CommitEdit();

    // Insert your business code to check modifications.

    // return true; if Saved/DontSave/NotChanged
    // return false; if Cancel
}

يجب أن تعمل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top