MVVM में एक पासवर्डबॉक्स से कैसे बांधें


251

मुझे P को बांधने में समस्या आ रही है asswordBox। ऐसा लगता है कि यह एक सुरक्षा जोखिम है लेकिन मैं एमवीवीएम पैटर्न का उपयोग कर रहा हूं इसलिए मैं इसे दरकिनार करना चाहता हूं। मुझे यहां कुछ दिलचस्प कोड मिला (क्या किसी ने इसका उपयोग किया है या कुछ इसी तरह का है?)

http://www.wpftutorial.net/PasswordBox.html

यह तकनीकी रूप से बहुत अच्छा लग रहा है, लेकिन मुझे इस बात का अनिश्चितता है कि पासवर्ड कैसे प्राप्त किया जाए।

मेरे पास मूल रूप से मेरे LoginViewModelलिए गुण हैं Usernameऔर PasswordUsernameठीक है और यह एक के रूप में काम कर रहा है TextBox

मैंने ऊपर बताए गए कोड का उपयोग किया है और इसमें प्रवेश किया है

<PasswordBox ff:PasswordHelper.Attach="True"
    ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>

जब मेरे पास PasswordBoxएक था TextBoxऔर Binding Path=Passwordतब मेरे पास संपत्ति LoginViewModelअपडेट हुई थी।

मेरा कोड बहुत सरल है, मूल रूप से मेरे पास Commandमेरे लिए है Button। जब मैं दबाता हूं CanLoginतो इसे कॉल किया जाता है और यदि यह सच है तो यह कॉल करता है Login
आप देख सकते हैं कि मैं अपनी संपत्ति की जाँच Usernameयहाँ कर रहा हूँ जो बहुत अच्छा काम करती है।

में Loginमैं अपने सेवा एक को साथ भेजा Usernameऔर Password, Usernameसे डेटा शामिल हैं मेरी Viewलेकिन PasswordहैNull|Empty

private DelegateCommand loginCommand;

public string Username { get; set; }
public string Password { get; set; }


public ICommand LoginCommand
{
    get
    {
        if (loginCommand == null)
        {
            loginCommand = new DelegateCommand(
                Login, CanLogin );
        }
        return loginCommand;
    }
}

private bool CanLogin()
{
    return !string.IsNullOrEmpty(Username);
}

private void Login()
{
    bool result = securityService.IsValidLogin(Username, Password);

    if (result) { }
    else { }
}

यही मैं कर रहा हूं

<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
         MinWidth="180" />

<PasswordBox ff:PasswordHelper.Attach="True" 
             ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>

मेरे पास मेरा है TextBox, यह कोई समस्या नहीं है, लेकिन मेरे ViewModelमें Passwordखाली है।

क्या मैं कुछ गलत कर रहा हूं या एक कदम याद आ रहा है?

मैं एक ब्रेकपाइंट रख दिया और यकीन है कि पर्याप्त कोड स्थिर सहायक कक्षा में प्रवेश लेकिन यह कभी अपडेट मेरे Passwordमें मेरा ViewModel


3
खैर यह पता चला है कि कोड काम नहीं करता था, लेकिन मैंने यहां एक वैकल्पिक कोड की कोशिश की और यह पूरी तरह से काम करता है। blog.functionalfun.net/2008/06/…
निशान

5
क्या दृश्यमॉडल से दृश्य को अलग करने के खिलाफ पूरे पासवर्डबॉक्स कंट्रोल में पास नहीं होता है?

जवाबों:


164

क्षमा करें, लेकिन आप इसे गलत कर रहे हैं।

लोगों को अपनी पलकों के अंदर निम्नलिखित सिक्योरिटी गाइडलाइन टैटू गुदवाना चाहिए:
मेमोरी में कभी भी सादे टेक्स्ट पासवर्ड न रखें।

संपत्ति के PasswordBoxलिए WPF / सिल्वरलाइट डीपी को उजागर नहीं करता है इसका कारण Passwordसुरक्षा से संबंधित है।
यदि WPF / सिल्वरलाइट के लिए एक DP रखना Passwordहोता है, तो इसके लिए फ्रेमवर्क की आवश्यकता होती है ताकि पासवर्ड को याद में अनएन्क्रिप्टेड रखा जा सके। जिसे काफी परेशान करने वाला सुरक्षा हमला वेक्टर माना जाता है। PasswordBoxका उपयोग करता है एन्क्रिप्टेड स्मृति (तरह की) और पासवर्ड का उपयोग करने के लिए एक ही रास्ता CLR संपत्ति के माध्यम से है।

मेरा सुझाव है कि PasswordBox.Passwordसीएलआर संपत्ति का उपयोग करते समय आप इसे किसी भी चर में या किसी भी संपत्ति के मूल्य के रूप में रखने से बचेंगे।
क्लाइंट मशीन RAM पर सादे पाठ में अपना पासवर्ड रखना एक सुरक्षा संख्या है।
इसलिए इससे छुटकारा पाएंpublic string Password { get; set; } तुम वहाँ उठ गए हो।

एक्सेस करते समय PasswordBox.Password, बस इसे बाहर निकालें और इसे सर्वर ASAP में शिप करें। पासवर्ड का मान इधर-उधर न रखें और इसे किसी अन्य ग्राहक मशीन के पाठ की तरह न समझें। स्मृति में स्पष्ट पाठ पासवर्ड न रखें।

मुझे पता है कि यह एमवीवीएम पैटर्न को तोड़ता है, लेकिन आपको कभी भी बांधना नहीं चाहिए PasswordBox.Password अटैच डीपी , अपने पासवर्ड को व्यूमॉडल या किसी अन्य समान शेंनिगन में स्टोर ।

यदि आप एक ओवर-आर्किटेक्चर समाधान की तलाश कर रहे हैं, तो यहां एक है:
1. IHavePasswordइंटरफ़ेस को एक विधि से बनाएं जो पासवर्ड स्पष्ट पाठ लौटाता है।
2. अपने UserControlएक IHavePasswordइंटरफ़ेस को लागू करें।
3. इंटरफ़ेस UserControlको लागू करने के रूप में अपने IoC के साथ उदाहरण को पंजीकृत IHavePasswordकरें।
4. जब आपके पासवर्ड की आवश्यकता के लिए एक सर्वर अनुरोध हो रहा है, तो IHavePasswordकार्यान्वयन के लिए अपने आईओसी को कॉल करें और केवल बहुत प्रतिष्ठित पासवर्ड प्राप्त करने की तुलना में।

बस मैं इसे लेती हूं।

- जस्टिन


19
क्या आप इस समस्या को हल करने के लिए WPF के लिए VM में SecureString का उपयोग नहीं कर सकते हैं? ऐसा नहीं लगता कि सिल्वरलाइट के लिए कुछ है।
ब्रायंट

35
मैं आपके इरादे और आपके द्वारा बताए गए संदेश से सहमत हूं लेकिन आपका उत्तर बताता है कि यदि आप इस दृष्टिकोण का अनुसरण करते हैं तो पासवर्ड स्ट्रिंग मेमोरी में नहीं है। पासवर्ड का मूल्य उस समय से होगा जब उपयोगकर्ता इसे टाइप करता है। अपना पासफ़्रेज़ रखने वाली संपत्ति को समाप्त करना एक अच्छा विचार है और आपके पासवर्ड की उन प्रतियों को सीमित कर देगा जो कचरा इकट्ठा करने वाले के पास छीनने के लिए चारों ओर छोड़ी जाती हैं या जो आपके कार्यक्रम के हिस्से के रूप में चल रहे अन्य प्रबंधित और अप्रबंधित कोड से मिल सकती हैं, लेकिन इसे पूरी तरह से छिपाएं नहीं।
इयानॉर्टन

182
अधिकांश मामलों के लिए, आपको उस स्तर की सुरक्षा की आवश्यकता नहीं है। जब पासवर्ड को चुराने के लिए इतने सारे तरीके हैं तो उस चीज़ को कठिन बनाने की क्या बात है? @Bryant ने कहा कि कम से कम WPSt को SecureString के उपयोग की अनुमति देनी चाहिए।
21

336
यदि बुरे लोगों के पास आपकी मशीन की रैम तक पहुंच है, तो आपके पास पासवर्ड चोरी करने की तुलना में उनके पास बड़े मुद्दे हैं।
कैमरन मैकफारलैंड

13
वर्षों से, मैं एक कस्टम उपयोगकर्ता नियंत्रण का उपयोग कर रहा हूं जो कि पासवर्डबॉक्स की तरह ही व्यवहार करता है, लेकिन केवल पाठ मूल्य को सिक्योरस्ट्रिंग के रूप में लौटाता है। हां, यह स्नूप को सादे पाठ में पासवर्ड प्रदर्शित करने से रोकता है। हालाँकि, SecureString का सादा पाठ मान अभी भी काफी आसानी से निकाला जा सकता है और केवल नौसिखिए हैक को रोक सकता है। यदि आपके सिस्टम में मुख्य लॉगर और स्नूप जैसे स्नीकर्स को नियोजित करने का जोखिम है, तो आपको अपने सिस्टम सुरक्षा पर फिर से मूल्यांकन करना चाहिए।
माइक क्रिश्चियन

199

मेरे 2 सेंट:

मैंने WPF और MVVM का उपयोग करके एक बार एक सामान्य लॉगिन डायलॉग (उपयोगकर्ता और पासवर्ड बॉक्स, प्लस "ओके" बटन) विकसित किया। मैंने केवल "ओके" बटन से जुड़ी कमांड के पैरामीटर के रूप में पासवर्डबॉक्स नियंत्रण को पास करके पासवर्ड बाइंडिंग समस्या को हल किया। इसलिए मेरे विचार में:

<PasswordBox Name="txtPassword" VerticalAlignment="Top" Width="120" />
<Button Content="Ok" Command="{Binding Path=OkCommand}"
   CommandParameter="{Binding ElementName=txtPassword}"/>

और ViewModel में, Executeसंलग्न कमांड की विधि इस प्रकार थी:

void Execute(object parameter)
{
    var passwordBox = parameter as PasswordBox;
    var password = passwordBox.Password;
    //Now go ahead and check the user name and password
}

यह MVVM पैटर्न का थोड़ा उल्लंघन करता है क्योंकि अब ViewModel कुछ जानता है कि दृश्य कैसे लागू किया जाता है, लेकिन उस विशेष परियोजना में मैं इसे बर्दाश्त कर सकता था। आशा है कि यह किसी के लिए भी उपयोगी है।


हैलो कोनामिमन, जब एक्सक्यूट विधि को कहा जाता है। मेरे दृष्टिकोण में मेरे पास एक वर्ग उपयोगकर्ता (लॉगिन, पास) और एक कमांड प्रमाणित है। मैं उस संदर्भ में एक्स्यूट का उपयोग कैसे कर सकता हूं?

3
बहुत उपयोगी, धन्यवाद। fyi, किसी को _loginCommand = new RelayCommand (param => Login (UserName, (PasswordBox) param)), param => CanLogIn) जैसे कुछ देखने की आदत हो सकती है;
चक रोस्टेंस

5
यह एक ठीक समाधान है, लेकिन पासवर्ड + पासवर्ड की पुष्टि कॉम्बो
जूलियन

हैलो कोनामिमन, मैं आपके समाधान का उपयोग कर रहा हूं लेकिन यह विंडोज 8.1 स्टोर ऐप पर काम नहीं करता है। मैंने यह सवाल पूछा है: stackoverflow.com/questions/26221594/…
VansFannel

2
इसके लिए धन्यवाद! इसने मुझे यूआई थ्रेड से डेटा को मुख्य प्रोग्राम थ्रेड में ले जाने के साथ एक बड़ी समस्या हल कर दी। सिक्योरस्ट्रिंग दृष्टिकोण को लागू करना सुनिश्चित करें, और ~ जितनी जल्दी हो सके पासवर्ड से छुटकारा पाएं ~। इसे फेंक दो। इसका निस्तारण करें। इसे साफ करो। आपको वही करना है जो आपको करना है। इसके अलावा, सुनिश्चित करें कि आप आईडीसोपायरी को लागू करते हैं।
स्टीवन सी। ब्रिटन

184

शायद मुझे कुछ याद आ रहा है, लेकिन ऐसा लगता है कि इनमें से अधिकांश समाधान चीजों को ओवरकम्प्लीट करते हैं और सुरक्षित प्रथाओं के साथ दूर करते हैं।

यह विधि MVVM पैटर्न का उल्लंघन नहीं करती है और पूरी सुरक्षा बनाए रखती है। हां, तकनीकी रूप से यह पीछे का कोड है, लेकिन यह "विशेष मामले" बंधन से अधिक कुछ नहीं है। ViewModel को अभी भी View कार्यान्वयन का कोई ज्ञान नहीं है, जो कि मेरे दिमाग में यह है यदि आप ViewModel में पासवर्डबॉक्स पास करने का प्रयास कर रहे हैं।

कोड के पीछे! = स्वचालित MVVM उल्लंघन। यह सब इस पर निर्भर करता है कि आप इसके साथ क्या करते हैं। इस मामले में, हम केवल मैन्युअल रूप से एक बाध्यकारी कोडिंग कर रहे हैं, इसलिए इसके सभी यूआई कार्यान्वयन का हिस्सा माना जाता है और इसलिए ठीक है।

ViewModel में, बस एक साधारण संपत्ति है। मैंने इसे "केवल" लिखा क्योंकि किसी भी कारण से इसे ViewModel के बाहर से पुनर्प्राप्त करने की आवश्यकता नहीं होनी चाहिए, लेकिन यह होना जरूरी नहीं है। ध्यान दें कि यह एक सिक्योरस्ट्रिंग है, न कि केवल एक स्ट्रिंग।

public SecureString SecurePassword { private get; set; }

Xaml में, आपने एक PasswordChanged ईवेंट हैंडलर सेट किया है।

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

पीछे कोड में:

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((dynamic)this.DataContext).SecurePassword = ((PasswordBox)sender).SecurePassword; }
}

इस पद्धति के साथ, आपका पासवर्ड हर समय एक सिक्योरस्ट्रीमिंग में रहता है और इसलिए अधिकतम सुरक्षा प्रदान करता है। यदि आप वास्तव में सुरक्षा के बारे में परवाह नहीं करते हैं या आपको डाउनस्ट्रीम विधि के लिए स्पष्ट पाठ पासवर्ड की आवश्यकता होती है, तो इसकी आवश्यकता होती है (ध्यान दें: अधिकांश .NET विधियों में पासवर्ड की आवश्यकता होती है जो एक SecureString विकल्प का भी समर्थन करता है, इसलिए आपको वास्तव में एक स्पष्ट पाठ पासवर्ड की आवश्यकता नहीं हो सकती है यहां तक ​​कि अगर आपको लगता है कि आप करते हैं), तो आप इसके बजाय बस पासवर्ड संपत्ति का उपयोग कर सकते हैं। ऐशे ही:

(ViewModel संपत्ति)

public string Password { private get; set; }

(कोड के पीछे)

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((dynamic)this.DataContext).Password = ((PasswordBox)sender).Password; }
}

यदि आप चीजों को दृढ़ता से रखना चाहते हैं, तो आप अपने ViewModel के इंटरफेस के साथ गतिशील (गतिशील) का स्थान ले सकते हैं। लेकिन वास्तव में, "सामान्य" डेटा बाइंडिंग को दृढ़ता से टाइप नहीं किया जाता है, इसलिए यह इतना बड़ा सौदा नहीं है।

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((IMyViewModel)this.DataContext).Password = ((PasswordBox)sender).Password; }
}

इसलिए सभी दुनिया के सर्वश्रेष्ठ - आपका पासवर्ड सुरक्षित है, आपके ViewModel में किसी अन्य संपत्ति की तरह एक संपत्ति है, और आपका दृश्य स्वयं है जिसमें कोई बाहरी संदर्भ आवश्यक नहीं है।


1
यह मुझे अच्छा लगता है! यदि आप सुरक्षा पक्ष पर सुपर सख्त होना चाहते थे तो यकीन नहीं होता कि यह कट जाएगा, लेकिन मेरे लिए यह एक आदर्श मध्य मैदान है। धन्यवाद!
jrich523

3
MVVM और व्यामोह के बारे में कठोर हठधर्मिता पर व्यावहारिकता के लिए धन्यवाद। महान काम करता है, धन्यवाद।
ब्रूस पीयरसन

2
SecureString का उदाहरण इस एक्सटेंशन के साथ बहुत अच्छा होगा blogs.msdn.com/b/fpintos/archive/2009/06/12/…
Ayman

1
वास्तव में अच्छा लगा। काश एमएस ने इस नियंत्रण के लिए सिर्फ सिक्योरस्ट्रिंग प्रकार का एक पासवर्ड डीपी जोड़ा होता।
कीथ हिल

1
यह सही उत्तर है, क्योंकि यह सुरक्षा और MVVM रखता है।
LoRdPMN

20

आप इस XAML का उपयोग कर सकते हैं:

<PasswordBox Name="PasswordBox">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="PasswordChanged">
            <i:InvokeCommandAction Command="{Binding PasswordChangedCommand}" CommandParameter="{Binding ElementName=PasswordBox}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</PasswordBox>

और यह कमांड निष्पादित विधि:

private void ExecutePasswordChangedCommand(PasswordBox obj)
{ 
   if (obj != null)
     Password = obj.Password;
}

3
FYI करेंxmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
XAMlMAX

पासवर्डबॉक्स को नाम देने की आवश्यकता के बिना: CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}"(नोट: नहीं RelativeSource Self )।
वंड्रा

यह समाधान MVVM पैटर्न का उल्लंघन करता है।
बायोनिककोड

13

यह मेरे लिए ठीक काम करता है।

<Button Command="{Binding Connect}" 
        CommandParameter="{Binding ElementName=MyPasswordBox}"/>

3
CommandParameter = "{बाइंडिंग ElementName = MyPasswordBox, Path = SecurePassword"} के बारे में क्या?
ल्यूकएन

2
ल्यूक, यह काम नहीं करता है (कम से कम मेरे लिए)। संभवतः उसी कारण से - SecurePassword निर्भरता गुण नहीं है।
vkrzv

यह मानते हुए कि यह ICommandदृश्य मॉडल में लागू है, यह समाधान MVVM पैटर्न का उल्लंघन करेगा।
बायोनिककोड

9

एमवीवीएम पैटर्न का उल्लंघन किए बिना एक सरल समाधान है, व्यूमेलोडल में एक घटना (या प्रतिनिधि) को पेश करना है जो पासवर्ड को काटता है।

में ViewModel :

public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;

इन EventArgs के साथ:

class HarvestPasswordEventArgs : EventArgs
{
    public string Password;
}

में देखें , पासवर्ड मूल्य में ViewModel और भरने बनाने पर घटना की सदस्यता लें।

_viewModel.HarvestPassword += (sender, args) => 
    args.Password = passwordBox1.Password;

में ViewModel , जब आप पासवर्ड की जरूरत है, आप ईवेंट को सक्रिय और वहाँ से पासवर्ड फसल कर सकते हैं:

if (HarvestPassword == null)
  //bah 
  return;

var pwargs = new HarvestPasswordEventArgs();
HarvestPassword(this, pwargs);

LoginHelpers.Login(Username, pwargs.Password);

एक चीज जो आप याद कर रहे हैं वह यह है कि किसी दृश्य मॉडल ईवेंट को देखने के लिए, आपको WeakEventManager<TEventSource, TEventArgs>मेमोरी लीक से बचने के लिए उपयोग करना चाहिए । कई बार दृश्य में दृश्यम के समान जीवनकाल नहीं होगा। WeakEventManager<IViewModel, EventArgs>.AddHandler(iViewModelInstance, nameof(IViewModel.Event), eventHandlerMethod);
टोड ए। स्टडेल

मैं इस समाधान को पसंद करता हूं, क्योंकि यह सरल है, एमवीवीएम का उल्लंघन नहीं करता है, इसके पीछे न्यूनतम कोड है, पासवर्डबॉक्स के सही उपयोग की अनुमति देता है (यदि आप इसके बजाय ureSecurePassword´ का उपयोग करते हैं)। इसके अलावा अब अन्य हार्वेस्टपासवर्ड विधियों को लागू करना सरल है (जैसे स्मार्टकार्ड ....)
मैट

8

मैंने विभिन्न समाधानों को देखते हुए बहुत समय बिताया। मैं सज्जाकार विचार पसंद नहीं करता था, व्यवहार सत्यापन यूआई को गड़बड़ करता है, कोड के पीछे ... वास्तव में?

सबसे अच्छा अभी तक एक कस्टम संलग्न संपत्ति से चिपके रहना और SecureStringअपने दृश्य मॉडल में अपनी संपत्ति से बांधना है । जब तक आप कर सकते हैं तब तक इसे वहां रखें। जब भी आपको सादे पासवर्ड तक त्वरित पहुंच की आवश्यकता होगी, तो नीचे दिए गए कोड का उपयोग करके इसे एक असुरक्षित स्ट्रिंग में अस्थायी रूप से परिवर्तित करें:

namespace Namespace.Extensions
{
    using System;
    using System.Runtime.InteropServices;
    using System.Security;

    /// <summary>
    /// Provides unsafe temporary operations on secured strings.
    /// </summary>
    [SuppressUnmanagedCodeSecurity]
    public static class SecureStringExtensions
    {
        /// <summary>
        /// Converts a secured string to an unsecured string.
        /// </summary>
        public static string ToUnsecuredString(this SecureString secureString)
        {
            // copy&paste from the internal System.Net.UnsafeNclNativeMethods
            IntPtr bstrPtr = IntPtr.Zero;
            if (secureString != null)
            {
                if (secureString.Length != 0)
                {
                    try
                    {
                        bstrPtr = Marshal.SecureStringToBSTR(secureString);
                        return Marshal.PtrToStringBSTR(bstrPtr);
                    }
                    finally
                    {
                        if (bstrPtr != IntPtr.Zero)
                            Marshal.ZeroFreeBSTR(bstrPtr);
                    }
                }
            }
            return string.Empty;
        }

        /// <summary>
        /// Copies the existing instance of a secure string into the destination, clearing the destination beforehand.
        /// </summary>
        public static void CopyInto(this SecureString source, SecureString destination)
        {
            destination.Clear();
            foreach (var chr in source.ToUnsecuredString())
            {
                destination.AppendChar(chr);
            }
        }

        /// <summary>
        /// Converts an unsecured string to a secured string.
        /// </summary>
        public static SecureString ToSecuredString(this string plainString)
        {
            if (string.IsNullOrEmpty(plainString))
            {
                return new SecureString();
            }

            SecureString secure = new SecureString();
            foreach (char c in plainString)
            {
                secure.AppendChar(c);
            }
            return secure;
        }
    }
}

सुनिश्चित करें कि आप GC को अपने UI तत्व को इकट्ठा करने की अनुमति देते हैं, इसलिए इस PasswordChangedघटना के लिए स्थैतिक घटना हैंडलर का उपयोग करने के आग्रह का विरोध करें PasswordBox। मैंने एक विसंगति की भी खोज की जहां नियंत्रण यूआई को अद्यतन नहीं कर रहा था जब SecurePasswordइसे स्थापित करने के लिए संपत्ति का उपयोग कर रहा था, यही कारण है कि मैं Passwordइसके बजाय पासवर्ड की नकल कर रहा हूं ।

namespace Namespace.Controls
{
    using System.Security;
    using System.Windows;
    using System.Windows.Controls;
    using Namespace.Extensions;

    /// <summary>
    /// Creates a bindable attached property for the <see cref="PasswordBox.SecurePassword"/> property.
    /// </summary>
    public static class PasswordBoxHelper
    {
        // an attached behavior won't work due to view model validation not picking up the right control to adorn
        public static readonly DependencyProperty SecurePasswordBindingProperty = DependencyProperty.RegisterAttached(
            "SecurePassword",
            typeof(SecureString),
            typeof(PasswordBoxHelper),
            new FrameworkPropertyMetadata(new SecureString(),FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AttachedPropertyValueChanged)
        );

        private static readonly DependencyProperty _passwordBindingMarshallerProperty = DependencyProperty.RegisterAttached(
            "PasswordBindingMarshaller",
            typeof(PasswordBindingMarshaller),
            typeof(PasswordBoxHelper),
            new PropertyMetadata()
        );

        public static void SetSecurePassword(PasswordBox element, SecureString secureString)
        {
            element.SetValue(SecurePasswordBindingProperty, secureString);
        }

        public static SecureString GetSecurePassword(PasswordBox element)
        {
            return element.GetValue(SecurePasswordBindingProperty) as SecureString;
        }

        private static void AttachedPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // we'll need to hook up to one of the element's events
            // in order to allow the GC to collect the control, we'll wrap the event handler inside an object living in an attached property
            // don't be tempted to use the Unloaded event as that will be fired  even when the control is still alive and well (e.g. switching tabs in a tab control) 
            var passwordBox = (PasswordBox)d;
            var bindingMarshaller = passwordBox.GetValue(_passwordBindingMarshallerProperty) as PasswordBindingMarshaller;
            if (bindingMarshaller == null)
            {
                bindingMarshaller = new PasswordBindingMarshaller(passwordBox);
                passwordBox.SetValue(_passwordBindingMarshallerProperty, bindingMarshaller);
            }

            bindingMarshaller.UpdatePasswordBox(e.NewValue as SecureString);
        }

        /// <summary>
        /// Encapsulated event logic
        /// </summary>
        private class PasswordBindingMarshaller
        {
            private readonly PasswordBox _passwordBox;
            private bool _isMarshalling;

            public PasswordBindingMarshaller(PasswordBox passwordBox)
            {
                _passwordBox = passwordBox;
                _passwordBox.PasswordChanged += this.PasswordBoxPasswordChanged;
            }

            public void UpdatePasswordBox(SecureString newPassword)
            {
                if (_isMarshalling)
                {
                    return;
                }

                _isMarshalling = true;
                try
                {
                    // setting up the SecuredPassword won't trigger a visual update so we'll have to use the Password property
                    _passwordBox.Password = newPassword.ToUnsecuredString();

                    // you may try the statement below, however the benefits are minimal security wise (you still have to extract the unsecured password for copying)
                    //newPassword.CopyInto(_passwordBox.SecurePassword);
                }
                finally
                {
                    _isMarshalling = false;
                }
            }

            private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e)
            {
                // copy the password into the attached property
                if (_isMarshalling)
                {
                    return;
                }

                _isMarshalling = true;
                try
                {
                    SetSecurePassword(_passwordBox, _passwordBox.SecurePassword.Copy());
                }
                finally
                {
                    _isMarshalling = false;
                }
            }
        }
    }
}

और XAML उपयोग:

<PasswordBox controls:PasswordBoxHelper.SecurePassword="{Binding LogonPassword, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">

दृश्य मॉडल में मेरी संपत्ति इस तरह दिखती है:

[RequiredSecureString]
public SecureString LogonPassword
{
   get
   {
       return _logonPassword;
   }
   set
   {
       _logonPassword = value;
       NotifyPropertyChanged(nameof(LogonPassword));
   }
}

RequiredSecureStringनिम्नलिखित तर्क है कि सिर्फ एक सरल कस्टम सत्यापनकर्ता है:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]    
public class RequiredSecureStringAttribute:ValidationAttribute
{
    public RequiredSecureStringAttribute()
        :base("Field is required")
    {            
    }

    public override bool IsValid(object value)
    {
        return (value as SecureString)?.Length > 0;
    }
}

यहां आप इसे रखते हैं। एक पूर्ण और परीक्षण किया गया शुद्ध एमवीवीएम समाधान।


7

मैंने यहाँ एक GIST पोस्ट किया है जो एक बाइंडेबल पासवर्ड बॉक्स है।

using System.Windows;
using System.Windows.Controls;

namespace CustomControl
{
    public class BindablePasswordBox : Decorator
    {
        /// <summary>
        /// The password dependency property.
        /// </summary>
        public static readonly DependencyProperty PasswordProperty;

        private bool isPreventCallback;
        private RoutedEventHandler savedCallback;

        /// <summary>
        /// Static constructor to initialize the dependency properties.
        /// </summary>
        static BindablePasswordBox()
        {
            PasswordProperty = DependencyProperty.Register(
                "Password",
                typeof(string),
                typeof(BindablePasswordBox),
                new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnPasswordPropertyChanged))
            );
        }

        /// <summary>
        /// Saves the password changed callback and sets the child element to the password box.
        /// </summary>
        public BindablePasswordBox()
        {
            savedCallback = HandlePasswordChanged;

            PasswordBox passwordBox = new PasswordBox();
            passwordBox.PasswordChanged += savedCallback;
            Child = passwordBox;
        }

        /// <summary>
        /// The password dependency property.
        /// </summary>
        public string Password
        {
            get { return GetValue(PasswordProperty) as string; }
            set { SetValue(PasswordProperty, value); }
        }

        /// <summary>
        /// Handles changes to the password dependency property.
        /// </summary>
        /// <param name="d">the dependency object</param>
        /// <param name="eventArgs">the event args</param>
        private static void OnPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs eventArgs)
        {
            BindablePasswordBox bindablePasswordBox = (BindablePasswordBox) d;
            PasswordBox passwordBox = (PasswordBox) bindablePasswordBox.Child;

            if (bindablePasswordBox.isPreventCallback)
            {
                return;
            }

            passwordBox.PasswordChanged -= bindablePasswordBox.savedCallback;
            passwordBox.Password = (eventArgs.NewValue != null) ? eventArgs.NewValue.ToString() : "";
            passwordBox.PasswordChanged += bindablePasswordBox.savedCallback;
        }

        /// <summary>
        /// Handles the password changed event.
        /// </summary>
        /// <param name="sender">the sender</param>
        /// <param name="eventArgs">the event args</param>
        private void HandlePasswordChanged(object sender, RoutedEventArgs eventArgs)
        {
            PasswordBox passwordBox = (PasswordBox) sender;

            isPreventCallback = true;
            Password = passwordBox.Password;
            isPreventCallback = false;
        }
    }
}

1
जब यह बुरा नहीं है, तो आप गद्दी और टेबिंडेक्स जैसी सरल विशेषताओं को सेट करने की क्षमता खो देते हैं
जूलियन

1
टेलर, मैंने गिस्ट को रेखांकित किया ताकि यह उत्तर में उपलब्ध हो। (यह लिंक-ओनली उत्तर की तरह लग रहा था अन्यथा .. बस इस तरह से हटाए जाने से बचने की कोशिश कर रहा है।) इनलाइन सामग्री के साथ गड़बड़ करने के लिए स्वतंत्र महसूस करें।
लिन क्रम्बलिंग

@ जूलियन लेकिन आप इसे स्टाइल के साथ ठीक कर सकते हैं। मैं इस समस्या को इसी तरह से हल करता हूं, लेकिन मैं ContentControlआप का उपयोग करता हूं तो बस एक पासवर्डबॉक्स का उपयोग कर सकते हैं एक सामग्री और शैली के रूप में कि एक्सएएमएल में आप फिट होते हैं। का उद्देश्य ContentControlसिर्फ इस PasswordChangedघटना की सदस्यता लेना है और दो-दिशात्मक बाइंडेबल संपत्ति को उजागर करना है। सब सब में, यह कोड की 65 लाइनें है और बहुत ज्यादा इस सजाने वर्ग क्या करता है। निम्नलिखित gist.github.com/leidegre/c7343b8c720000fe3132
जॉन लेडिग्रेन

6

यह कार्यान्वयन थोड़ा अलग है। आप ViewModel में किसी प्रॉपर्टी के बाइंडिंग व्यू के लिए एक पासवर्डबॉक्स पास करते हैं, यह किसी भी कमांड परम का उपयोग नहीं करता है। ViewModel दृश्य से अनभिज्ञ रहता है। मेरे पास एक वीबी बनाम 2010 प्रोजेक्ट है जिसे स्काईड्राइव से डाउनलोड किया जा सकता है। Wpf MvvM PassWordBox Example.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9A8DD73 .511!

जिस तरह से मैं एक Wpf MvvM Application में PasswordBox का उपयोग कर रहा हूं वह बहुत सरल है और मेरे लिए अच्छा काम करता है। इसका मतलब यह नहीं है कि मुझे लगता है कि यह सही तरीका है या सबसे अच्छा तरीका है। यह PasswordBox और MvvM पैटर्न का उपयोग करने का एक कार्यान्वयन है।

मूल रूप से आप एक सार्वजनिक रूप से आसानी से संपत्ति बनाते हैं जिसे व्यू पासवर्डबॉक्स के रूप में बाँध सकता है (वास्तविक नियंत्रण) उदाहरण:

Private _thePassWordBox As PasswordBox
Public ReadOnly Property ThePassWordBox As PasswordBox
    Get
        If IsNothing(_thePassWordBox) Then _thePassWordBox = New PasswordBox
        Return _thePassWordBox
    End Get
End Property

मैं संपत्ति के स्वयं प्रारंभ करने के लिए एक बैकिंग फ़ील्ड का उपयोग करता हूं।

तब Xaml से आप एक ContentControl या एक नियंत्रण कंटेनर उदाहरण की सामग्री को बांधते हैं:

 <ContentControl Grid.Column="1" Grid.Row="1" Height="23" Width="120" Content="{Binding Path=ThePassWordBox}" HorizontalAlignment="Center" VerticalAlignment="Center" />

वहां से आपको पासवर्डबॉक्स पर पूर्ण नियंत्रण है मैं लॉगिन या जब भी आप पासवर्ड चाहते हैं, तो पासवर्ड मान वापस करने के लिए एक पासवर्डअवर (स्ट्रिंग का एक फ़ंक्शन) का उपयोग करें। उदाहरण में मेरे पास जेनेरिक उपयोगकर्ता ऑब्जेक्ट मॉडल में एक सार्वजनिक संपत्ति है। उदाहरण:

Public Property PasswordAccessor() As Func(Of String)

उपयोगकर्ता ऑब्जेक्ट में पासवर्ड स्ट्रिंग प्रॉपर्टी को किसी भी बैकिंग स्टोर के बिना आसानी से पढ़ा जाता है, यह सिर्फ पासवर्डबॉक्स से पासवर्ड लौटाता है। उदाहरण:

Public ReadOnly Property PassWord As String
    Get
        Return If((PasswordAccessor Is Nothing), String.Empty, PasswordAccessor.Invoke())
    End Get
End Property

फिर ViewModel में मैं यह सुनिश्चित करता हूं कि Accessor बनाया गया है और PasswordBox.Password प्रॉपर्टी के उदाहरण पर सेट किया गया है:

Public Sub New()
    'Sets the Accessor for the Password Property
    SetPasswordAccessor(Function() ThePassWordBox.Password)
End Sub

Friend Sub SetPasswordAccessor(ByVal accessor As Func(Of String))
    If Not IsNothing(VMUser) Then VMUser.PasswordAccessor = accessor
End Sub

जब मुझे लॉगिन के लिए पासवर्ड स्ट्रिंग की आवश्यकता होती है, तो मुझे केवल उपयोगकर्ता ऑब्जेक्ट पासवर्ड की संपत्ति मिलती है जो वास्तव में पासवर्ड को हथियाने के लिए फ़ंक्शन को आमंत्रित करती है और इसे वापस करती है, फिर वास्तविक पासवर्ड उपयोगकर्ता ऑब्जेक्ट द्वारा संग्रहीत नहीं किया जाता है। उदाहरण: ViewModel में होगा

Private Function LogIn() as Boolean
    'Make call to your Authentication methods and or functions. I usally place that code in the Model
    Return AuthenticationManager.Login(New UserIdentity(User.UserName, User.Password)
End Function

इससे हो जाना चाहिए। ViewModel को व्यू के नियंत्रण के किसी भी ज्ञान की आवश्यकता नहीं है। दृश्य बस ViewModel में संपत्ति को बांधता है, किसी छवि या अन्य संसाधन को देखने के बंधन से अलग नहीं है। इस मामले में कि संसाधन (संपत्ति) सिर्फ एक उपयोगकर्ता के लिए होता है। यह परीक्षण के लिए अनुमति देता है क्योंकि ViewModel संपत्ति बनाता है और उसका मालिक है और संपत्ति दृश्य से स्वतंत्र है। सुरक्षा के लिए मुझे नहीं पता कि यह कार्यान्वयन कितना अच्छा है। लेकिन एक फंक्शन का उपयोग करके मूल्य केवल संपत्ति में संग्रहीत नहीं होता है जो संपत्ति द्वारा एक्सेस किया जाता है।


6

MVVM को तोड़े बिना ओपी समस्या को हल करने के लिए, मैं कस्टम वैल्यू कन्वर्टर और वैल्यू (पासवर्ड) के लिए एक रैपर का उपयोग करूंगा, जिसे पासवर्ड बॉक्स से पुनर्प्राप्त किया जाना है।

public interface IWrappedParameter<T>
{
    T Value { get; }
}

public class PasswordBoxWrapper : IWrappedParameter<string>
{
    private readonly PasswordBox _source;

    public string Value
    {
        get { return _source != null ? _source.Password : string.Empty; }
    }

    public PasswordBoxWrapper(PasswordBox source)
    {
        _source = source;
    }
}

public class PasswordBoxConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Implement type and value check here...
        return new PasswordBoxWrapper((PasswordBox)value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new InvalidOperationException("No conversion.");
    }
}

दृश्य मॉडल में:

public string Username { get; set; }

public ICommand LoginCommand
{
    get
    {
        return new RelayCommand<IWrappedParameter<string>>(password => { Login(Username, password); });
    }
}

private void Login(string username, string password)
{
    // Perform login here...
}

क्योंकि दृश्य मॉडल का उपयोग करता है IWrappedParameter<T>, इसके बारे में PasswordBoxWrapperन तो ज्ञान होने की आवश्यकता है और न ही PasswordBoxConverter। इस तरह आप अलग कर सकते हैंPasswordBox ऑब्जेक्ट को मॉडल से और MVVM पैटर्न को नहीं तोड़ सकते।

दृश्य में:

<Window.Resources>
    <h:PasswordBoxConverter x:Key="PwdConverter" />
</Window.Resources>
...
<PasswordBox Name="PwdBox" />
<Button Content="Login" Command="{Binding LoginCommand}"
        CommandParameter="{Binding ElementName=PwdBox, Converter={StaticResource PwdConverter}}" />

बहुत ही सुंदर समाधान imo। मैं इस पर मेरा आधारित है। एकमात्र अंतर: मैं स्ट्रिंग पासवर्ड के बजाय फ़ंक्शन को लॉगिन करने के लिए SecureString SecurePassword पास करता हूं। ताकि याददाश्त के इर्द-गिर्द उड़ने वाले पासविहीन तार न हों।
मुझे

यह एक समय हो गया है, लेकिन मुझे लगता है कि यह मेरे RelayCommand के कारण काम करने के लिए नहीं मिल सकता है। क्या तुम्हारा मन जुड़ जाएगा?
इकेनरवाल

5

हालांकि मैं मानता हूं कि पासवर्ड को कहीं भी संग्रहीत करने से बचना महत्वपूर्ण है, फिर भी मुझे बिना किसी दृश्य के दृश्य मॉडल को तुरंत देखने और इसके खिलाफ मेरे परीक्षणों को निष्पादित करने की क्षमता की आवश्यकता है।

मेरे लिए काम करने वाला समाधान पासवर्ड मॉडल के साथ PasswordBox.Password फ़ंक्शन को पंजीकृत करने के लिए था, और लॉगिन कोड को निष्पादित करते समय दृश्य मॉडल इसे लागू करता है।

यह करता है दृश्य की codebehind में कोड की एक पंक्ति मतलब है।

तो, मेरे Login.xaml में मेरे पास है

<PasswordBox x:Name="PasswordBox"/>

और Login.xaml.cs में मेरे पास है

LoginViewModel.PasswordHandler = () => PasswordBox.Password;

उसके बाद LoginViewModel.cs में मेरे पास पासवर्डहैंडलर परिभाषित है

public Func<string> PasswordHandler { get; set; }

और जब लॉगिन करने की आवश्यकता हो तो कोड हैंडलर को देखने से पासवर्ड प्राप्त करने के लिए आमंत्रित करता है ...

bool loginResult = Login(Username, PasswordHandler());

इस तरह, जब मैं viewmodel का परीक्षण करना चाहता हूं तो मैं पासवर्डहैंडलर को एक अनाम विधि पर सेट कर सकता हूं जो मुझे परीक्षण में उपयोग किए जाने वाले किसी भी पासवर्ड को वितरित करने देता है।


4

मुझे लगा कि मैं मिश्रण में अपना घोल फेंक दूंगा, क्योंकि यह इतना सामान्य मुद्दा है ... और बहुत सारे विकल्प होना हमेशा एक अच्छी बात है।

मैंने बस एक PasswordBoxमें लपेटा UserControlऔर एक DependencyPropertyको लागू करने में सक्षम होने के लिए बाध्य किया। मैं वह सब कुछ कर रहा हूं जो मैं मेमोरी में किसी भी स्पष्ट पाठ को संग्रहीत करने से बच सकता हूं, इसलिए सब कुछ एक SecureStringऔर PasswordBox.Passwordसंपत्ति के माध्यम से किया जाता है । foreachलूप के दौरान , प्रत्येक वर्ण उजागर हो जाता है, लेकिन यह बहुत संक्षिप्त है। ईमानदारी से, यदि आप इस संक्षिप्त विवरण से समझौता करने के लिए अपने WPF आवेदन के बारे में चिंतित हैं, तो आपको बड़े सुरक्षा मुद्दे मिल गए हैं जिन्हें संभालना चाहिए।

इसका सौंदर्य यह है कि आप किसी भी MVVM नियमों को नहीं तोड़ रहे हैं, यहां तक ​​कि "शुद्धतावादी" भी, क्योंकि यह एक है UserControl, इसलिए इसे कोड-पीछे रखने की अनुमति है। जब आप इसे उपयोग कर रहे हैं, तो आप के बीच शुद्ध संचार हो सकता है Viewऔर ViewModelअपने बिना VideModelके किसी भी हिस्से के बारे में पता किया जा रहा है Viewया पासवर्ड का स्रोत। बस सुनिश्चित करें कि आप SecureStringअपने में बंध रहे हैं ViewModel

BindablePasswordBox.xaml

<UserControl x:Class="BK.WPF.CustomControls.BindanblePasswordBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" d:DesignHeight="22" d:DesignWidth="150">
    <PasswordBox x:Name="PswdBox"/>
</UserControl>

BindablePasswordBox.xaml.cs (संस्करण 1 - कोई दो-तरफ़ा बाइंडिंग समर्थन नहीं है।)

using System.ComponentModel;
using System.Security;
using System.Windows;
using System.Windows.Controls;

namespace BK.WPF.CustomControls
{
    public partial class BindanblePasswordBox : UserControl
    {
        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.Register("Password", typeof(SecureString), typeof(BindanblePasswordBox));

        public SecureString Password
        {
            get { return (SecureString)GetValue(PasswordProperty); }
            set { SetValue(PasswordProperty, value); }
        }

        public BindanblePasswordBox()
        {
            InitializeComponent();
            PswdBox.PasswordChanged += PswdBox_PasswordChanged;
        }

        private void PswdBox_PasswordChanged(object sender, RoutedEventArgs e)
        {
            var secure = new SecureString();
            foreach (var c in PswdBox.Password)
            {
                secure.AppendChar(c);
            }
            Password = secure;
        }
    }
}

संस्करण 1 का उपयोग:

<local:BindanblePasswordBox Width="150" HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Password="{Binding Password, Mode=OneWayToSource}"/>

BindablePasswordBox.xaml.cs (संस्करण 2 - दो-तरफ़ा बाइंडिंग समर्थन है।)

public partial class BindablePasswordBox : UserControl
{
    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(SecureString), typeof(BindablePasswordBox),
        new PropertyMetadata(PasswordChanged));

    public SecureString Password
    {
        get { return (SecureString)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    public BindablePasswordBox()
    {
        InitializeComponent();
        PswdBox.PasswordChanged += PswdBox_PasswordChanged;
    }

    private void PswdBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
        var secure = new SecureString();
        foreach (var c in PswdBox.Password)
        {
            secure.AppendChar(c);
        }
        if (Password != secure)
        {
            Password = secure;
        }
    }

    private static void PasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var pswdBox = d as BindablePasswordBox;
        if (pswdBox != null && e.NewValue != e.OldValue)
        {
            var newValue = e.NewValue as SecureString;
            if (newValue == null)
            {
                return;
            }

            var unmanagedString = IntPtr.Zero;
            string newString;
            try
            {
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(newValue);
                newString = Marshal.PtrToStringUni(unmanagedString);
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
            }

            var currentValue = pswdBox.PswdBox.Password;
            if (currentValue != newString)
            {
                pswdBox.PswdBox.Password = newString;
            }
        }
    }
}

संस्करण 2 का उपयोग:

<local:BindanblePasswordBox Width="150" HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Password="{Binding Password, Mode=TwoWay}"/>

मैंने इसे लागू करने की कोशिश की है, लेकिन जब आप यूआई पर पासवर्ड अपडेट करते हैं तो आपको एक अनंत लूप मिलता है; क्योंकि if (Password != secure)हमेशा गलत होगा क्योंकि SecureString के बराबर नहीं है। कोई विचार?
सिमोनलेक्जेंडर 2005


2

मैंने इस पद्धति का उपयोग किया और पासवर्ड बॉक्स को पारित किया, हालांकि यह MVVM का उल्लंघन करता है यह मेरे लिए आवश्यक था क्योंकि मैं अपने शेल के भीतर अपने लॉगिन के लिए डेटा टेम्पलेट के साथ एक सामग्री नियंत्रण का उपयोग कर रहा था जो एक जटिल शेल एनवायरमेंट है। इसलिए शेल के पीछे कोड को एक्सेस करना बकवास होगा।

पासवर्डबॉक्स पास करना मुझे लगता है कि कोड से कंट्रोलिंग एक्सेस करना उतना ही आसान है जितना मैं जानता हूं। मैं पासवर्ड से सहमत हूं, स्मृति में न रखें आदि। इस कार्यान्वयन में मेरे पास दृश्य मॉडल में पासवर्ड के लिए संपत्ति नहीं है।

बटन कमान

Command="{Binding Path=DataContext.LoginCommand, ElementName=MyShell}" CommandParameter="{Binding ElementName=PasswordBox}"

ViewModel

private void Login(object parameter)
{
    System.Windows.Controls.PasswordBox p = (System.Windows.Controls.PasswordBox)parameter;
    MessageBox.Show(p.Password);
}

यह एमवीवीएम पैटर्न का स्पष्ट उल्लंघन है। पैटर्न दृश्य मॉडल में नियंत्रण को संभालने की अनुमति नहीं देता है।
बायोनिककोड

2

मेरे लिए, ये दोनों बातें गलत हैं:

  • स्पष्ट पाठ पासवर्ड गुण लागू करना
  • PasswordBoxViewModel में एक कमांड पैरामीटर के रूप में भेजा जा रहा है

CO में स्टीव द्वारा बताए गए SecurePassword (SecureString उदाहरण) को स्थानांतरित करना स्वीकार्य लगता है। मैं Behaviorsपीछे कोड करना पसंद करता हूं , और मुझे व्यूमोडेल से पासवर्ड रीसेट करने में सक्षम होने की अतिरिक्त आवश्यकता भी थी।

Xaml ( PasswordViewModel संपत्ति है):

<PasswordBox>
    <i:Interaction.Behaviors>
        <behaviors:PasswordBinding BoundPassword="{Binding Password, Mode=TwoWay}" />
    </i:Interaction.Behaviors>
</PasswordBox>

व्यवहार:

using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;

namespace Evidence.OutlookIntegration.AddinLogic.Behaviors
{
    /// <summary>
    /// Intermediate class that handles password box binding (which is not possible directly).
    /// </summary>
    public class PasswordBoxBindingBehavior : Behavior<PasswordBox>
    {
        // BoundPassword
        public SecureString BoundPassword { get { return (SecureString)GetValue(BoundPasswordProperty); } set { SetValue(BoundPasswordProperty, value); } }
        public static readonly DependencyProperty BoundPasswordProperty = DependencyProperty.Register("BoundPassword", typeof(SecureString), typeof(PasswordBoxBindingBehavior), new FrameworkPropertyMetadata(OnBoundPasswordChanged));

        protected override void OnAttached()
        {
            this.AssociatedObject.PasswordChanged += AssociatedObjectOnPasswordChanged;
            base.OnAttached();
        }

        /// <summary>
        /// Link up the intermediate SecureString (BoundPassword) to the UI instance
        /// </summary>
        private void AssociatedObjectOnPasswordChanged(object s, RoutedEventArgs e)
        {
            this.BoundPassword = this.AssociatedObject.SecurePassword;
        }

        /// <summary>
        /// Reacts to password reset on viewmodel (ViewModel.Password = new SecureString())
        /// </summary>
        private static void OnBoundPasswordChanged(object s, DependencyPropertyChangedEventArgs e)
        {
            var box = ((PasswordBoxBindingBehavior)s).AssociatedObject;
            if (box != null)
            {
                if (((SecureString)e.NewValue).Length == 0)
                    box.Password = string.Empty;
            }
        }

    }
}

2

मेरे जैसे पूर्ण newbies के लिए, यहां Konamimanऊपर दिए गए सुझाव का एक पूरा कार्य नमूना है । धन्यवाद Konamiman

XAML

    <PasswordBox x:Name="textBoxPassword"/>
    <Button x:Name="buttonLogin" Content="Login"
            Command="{Binding PasswordCommand}"
            CommandParameter="{Binding ElementName=textBoxPassword}"/> 

ViewModel

public class YourViewModel : ViewModelBase
{
    private ICommand _passwordCommand;
    public ICommand PasswordCommand
    {
        get {
            if (_passwordCommand == null) {
                _passwordCommand = new RelayCommand<object>(PasswordClick);
            }
            return _passwordCommand;
        }
    }

    public YourViewModel()
    {
    }

    private void PasswordClick(object p)
    {
        var password = p as PasswordBox;
        Console.WriteLine("Password is: {0}", password.Password);
    }
}

यह एमवीवीएम पैटर्न का स्पष्ट उल्लंघन है। पैटर्न दृश्य मॉडल में नियंत्रण को संभालने की अनुमति नहीं देता है।
बायोनिककोड

1

जैसा कि आप देख सकते हैं कि मैं पासवर्ड के लिए बाध्य हूं, लेकिन हो सकता है कि यह इसे स्थिर वर्ग से बांध दे।

यह एक संलग्न संपत्ति है । इस तरह की संपत्ति को किसी भी प्रकार से लागू किया जा सकता है DependencyObject, न कि केवल उस प्रकार में, जिसमें यह घोषित किया गया है। इसलिए भले ही इसे PasswordHelperस्टैटिक क्लास में घोषित किया गया हो , पर इसे लागू किया जाता हैPasswordBox पर पर आप इसका उपयोग करते हैं।

इस संलग्न संपत्ति का उपयोग करने के लिए, आपको इसे Passwordअपने ViewModel में संपत्ति से बांधने की आवश्यकता है :

<PasswordBox w:PasswordHelper.Attach="True" 
         w:PasswordHelper.Password="{Binding Password}"/>

1

मैंने ऐसा किया है:

XAML:

<PasswordBox x:Name="NewPassword" PasswordChanged="NewPassword_PasswordChanged"/>
<!--change tablenameViewSource: yours!-->
<Grid DataContext="{StaticResource tablenameViewSource}" Visibility="Hidden">
        <TextBox x:Name="Password" Text="{Binding password, Mode=TwoWay}"/>
</Grid>

सी#:

private void NewPassword_PasswordChanged(object sender, RoutedEventArgs e)
    {
        try
        {
           //change tablenameDataTable: yours! and tablenameViewSource: yours!
           tablenameDataTable.Rows[tablenameViewSource.View.CurrentPosition]["password"] = NewPassword.Password;
        }
        catch
        {
            this.Password.Text = this.NewPassword.Password;
        }
    }

इससे मेरा काम बनता है!


आप मुझे एक अच्छा विचार दें। :)
आंद्रे मेंडोंका

1

जैसा कि वीएम को देखने से पहले अनजान होना चाहिए, लेकिन पूरे पासवर्डबॉक्स को पार करना सबसे सरल दृष्टिकोण की तरह दिखता है। तो शायद पासवर्डबॉक्स को पास करने के बजाय पासवर्ड से संपत्ति को निकालने के लिए प्रतिबिंब का उपयोग करें। इस मामले में वीएम को संपत्ति के पासवर्ड के साथ कुछ प्रकार के पासवर्ड कंटेनर की उम्मीद है (मैं एमवीएमएम लाइट-टूलकिट से रिलेकेमैंड्स का उपयोग कर रहा हूं):

public RelayCommand<object> SignIn
{
    get
    {
        if (this.signIn == null)
        {
            this.signIn = new RelayCommand<object>((passwordContainer) => 
                {
                    var password = passwordContainer.GetType().GetProperty("Password").GetValue(passwordContainer) as string;
                    this.authenticationService.Authenticate(this.Login, password);
                });
        }

        return this.signIn;
    }
}

यह आसानी से अनाम वर्ग के साथ परीक्षण किया जा सकता है:

var passwordContainer = new
    {
        Password = "password"
    };

टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
शमूएल एलवाई

1

विंडोज़ यूनिवर्सल ऐप में

आप इस कोड को "पासवर्ड" और मॉडल व्यू के साथ बाइंडिंग के साथ उपयोग कर सकते हैं

 <PasswordBox x:Uid="PasswordBox" Password="{Binding Waiter.Password, Mode=TwoWay}" Name="txtPassword" HorizontalAlignment="Stretch" Margin="50,200,50,0" VerticalAlignment="Top"/>


1

किसी के लिए भी जो इस कार्यान्वयन के जोखिमों से अवगत है, आपके ViewModel में पासवर्ड सिंक करने के लिए बस मोड = OneWayToSource जोड़ें ।

XAML

<PasswordBox
    ff:PasswordHelper.Attach="True"
    ff:PasswordHelper.Password="{Binding Path=Password, Mode=OneWayToSource}" />

सिर्फ क्यों नहीं OneWayToSource?
बीके

@BK ने मेरे उत्तर को संपादित किया। धन्यवाद।
केविन

1
बाइंडिंग ब्रेसेस के अंदर मोड नहीं होना चाहिए?
Mat

@ म यप। धन्यवाद।
केविन

1

यहाँ मेरा उस पर ले लो:

  1. पासवर्ड को बांधने के लिए संलग्न संपत्ति का उपयोग करना पासवर्ड को सुरक्षित करने के उद्देश्य को पराजित करता है। पासवर्ड बॉक्स की पासवर्ड संपत्ति एक कारण के लिए बाध्यकारी नहीं है।

  2. पासवर्ड बॉक्स को कमांड पैरामीटर के रूप में पास करने से व्यूमॉडल को नियंत्रण के बारे में पता चल जाएगा। यदि आप अपना ViewModel पुन: प्रयोज्य क्रॉस प्लेटफॉर्म बनाने की योजना बनाते हैं तो यह काम नहीं करेगा। अपने दृश्य या किसी अन्य नियंत्रण से अपने वीएम को अवगत न कराएं।

  3. मुझे नहीं लगता कि एक नई संपत्ति शुरू करना, एक इंटरफ़ेस, पासवर्ड परिवर्तित घटनाओं की सदस्यता लेना या पासवर्ड प्रदान करने के एक सरल कार्य के लिए कोई अन्य जटिल चीजें आवश्यक हैं।

XAML

<PasswordBox x:Name="pbPassword" />
<Button Content="Login" Command="{Binding LoginCommand}" x:Name="btnLogin"/>

पीछे कोड - पीछे कोड का उपयोग करना आवश्यक नहीं है MVVM का उल्लंघन। जब तक आप इसमें कोई व्यावसायिक तर्क नहीं रखते हैं।

btnLogin.CommandParameter = new Func<string>(()=>pbPassword.Password); 

ViewModel

LoginCommand = new RelayCommand<Func<string>>(getpwd=> { service.Login(username, getpwd()); });

0

आप WPF अनुप्रयोग फ्रेमवर्क (WAF) के ViewModel नमूना आवेदन में पासवर्डबॉक्स के लिए एक समाधान पाते हैं परियोजना ।

हालांकि, जस्टिन सही है। पासवर्ड को View और ViewModel के बीच सादे पाठ के रूप में पास न करें। इसके बजाय SecureString का उपयोग करें (MSDN पासवर्डबॉक्स देखें)।


2
जिस तरह से WAF के Pop3SettingsView में उपयोग किया जाता है वह हास्यास्पद है। पासवर्डबॉक्स पासवर्डबॉक्स = (पासवर्डबॉक्स) प्रेषक; if (ViewModel! = null) {ViewModel.Pop3Password = passwordBox.Password; } Pop3Password of ViewModel स्ट्रिंग प्रॉपर्टी है। इसलिए, यह सुरक्षित नहीं है .. संलग्न संपत्ति का उपयोग करने के लिए बेहतर है
माइकल सिंक

0

मैंने डेटा वर्ग को पासवर्ड लिखने के लिए एक मध्यस्थ चेक द्वारा उप नाम के बाद एक प्रमाणीकरण चेक का उपयोग किया था, जो व्यू में (जो एक प्रमाणीकरण चेक को लागू करता है)।

यह एक सही समाधान नहीं है; हालाँकि, इसने पासवर्ड को स्थानांतरित न कर पाने की मेरी समस्या को दूर कर दिया।


0

मैं succinct MVVM के अनुकूल समाधान का उपयोग कर रहा हूं जिसका अभी तक उल्लेख नहीं किया गया है। सबसे पहले, मैं XAML में पासवर्डबॉक्स को नाम देता हूं:

<PasswordBox x:Name="Password" />

फिर मैं निर्माणकर्ता में एक एकल विधि कॉल जोड़ता हूं:

public LoginWindow()
{
    InitializeComponent();
    ExposeControl<LoginViewModel>.Expose(this, view => view.Password,
        (model, box) => model.SetPasswordBox(box));
}

और बस। जब यह अलग हो जाता है तो डेटा मॉडल को एक दृश्य से जोड़ा जाता है, जब वह डेटा कॉन्टेक्स्ट और किसी अन्य अधिसूचना के माध्यम से सूचना प्राप्त करेगा। इस सूचना की सामग्री लंबोदर के माध्यम से कॉन्फ़िगर करने योग्य है, लेकिन आमतौर पर यह दृश्य मॉडल पर सिर्फ एक सेटर या विधि कॉल है, एक पैरामीटर के रूप में समस्याग्रस्त नियंत्रण से गुजर रहा है।

चाइल्ड कंट्रोल के बजाय व्यू एक्सपोज इंटरफेस होने से इसे आसानी से एमवीवीएम-फ्रेंडली बनाया जा सकता है।

उपरोक्त कोड मेरे ब्लॉग पर प्रकाशित सहायक वर्ग पर निर्भर करता है ।


0

मैंने इस काम को पाने के लिए उम्र बिताई। अंत में, मैंने हार मान ली और बस DevExpress से पासवर्डबॉक्स का उपयोग किया।

यह अब तक का सबसे सरल उपाय है, क्योंकि यह बिना किसी भयानक चाल को खींचे बाध्यकारी है।

DevExpress वेबसाइट पर समाधान

रिकॉर्ड के लिए, मैं किसी भी तरह से DevExpress से संबद्ध नहीं हूं।


0

<UserControl x:Class="Elections.Server.Handler.Views.LoginView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             xmlns:cal="http://www.caliburnproject.org"
             mc:Ignorable="d" 
             Height="531" Width="1096">
    <ContentControl>
        <ContentControl.Background>
            <ImageBrush/>
        </ContentControl.Background>
        <Grid >
            <Border BorderBrush="#FFABADB3" BorderThickness="1" HorizontalAlignment="Left" Height="23" Margin="900,100,0,0" VerticalAlignment="Top" Width="160">
                <TextBox TextWrapping="Wrap"/>
            </Border>
            <Border BorderBrush="#FFABADB3" BorderThickness="1" HorizontalAlignment="Left" Height="23" Margin="900,150,0,0" VerticalAlignment="Top" Width="160">
                <PasswordBox x:Name="PasswordBox"/>
            </Border>
            <Button Content="Login" HorizontalAlignment="Left" Margin="985,200,0,0" VerticalAlignment="Top" Width="75">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <cal:ActionMessage MethodName="Login">
                            <cal:Parameter Value="{Binding ElementName=PasswordBox}" />
                        </cal:ActionMessage>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>

        </Grid>
    </ContentControl>
</UserControl>

using System;
using System.Windows;
using System.Windows.Controls;
using Caliburn.Micro;

namespace Elections.Server.Handler.ViewModels
{
    public class LoginViewModel : PropertyChangedBase
    {
        MainViewModel _mainViewModel;
        public void SetMain(MainViewModel mainViewModel)
        {
            _mainViewModel = mainViewModel;
        }

        public void Login(Object password)
        {
            var pass = (PasswordBox) password;
            MessageBox.Show(pass.Password);

            //_mainViewModel.ScreenView = _mainViewModel.ControlPanelView;
            //_mainViewModel.TitleWindow = "Panel de Control";
            //HandlerBootstrapper.Title(_mainViewModel.TitleWindow);
        }
    }
}

;) आसान!


0

यह बहुत सरल है । पासवर्ड के लिए एक और प्रॉपर्टी बनाएं और टेक्स्टबॉक्स के साथ इसे बांधें

लेकिन सभी इनपुट ऑपरेशन वास्तविक पासवर्ड संपत्ति के साथ प्रदर्शन करते हैं

निजी स्ट्रिंग _Password;

    public string PasswordChar
    {
        get
        {
            string szChar = "";

            foreach(char szCahr in _Password)
            {
                szChar = szChar + "*";
            }

            return szChar;
        }

        set
        {
            _PasswordChar = value; NotifyPropertyChanged();
        }
    }

सार्वजनिक स्ट्रिंग पासवर्ड {get {रिटर्न _Password; }

        set
        {
            _Password = value; NotifyPropertyChanged();
            PasswordChar = _Password;
        }
    }


पासवर्ड बॉक्स बाइंड करने योग्य नहीं है क्योंकि हम पासवर्ड को एक स्पष्ट स्ट्रिंग में संग्रहीत नहीं करना चाहते हैं। स्ट्रिंग अपरिवर्तनीय है और हमें यकीन नहीं है कि यह स्मृति में कब तक रहेगा।
लांस

0

अच्छी तरह से मेरे जवाब सिर्फ MVVM पैटर्न के लिए अधिक सरल है

वर्ग के दृष्टिकोण में

public string password;

PasswordChangedCommand = new DelegateCommand<RoutedEventArgs>(PasswordChanged);

Private void PasswordChanged(RoutedEventArgs obj)

{

    var e = (WatermarkPasswordBox)obj.OriginalSource;

    //or depending or what are you using

    var e = (PasswordBox)obj.OriginalSource;

    password =e.Password;

}

पासवर्डबॉक्स की विजुअल प्रॉपर्टी जो जीतती है या वॉटरमार्कसपासबॉक्स जो कि XCeedtoolkit प्रदान करता है, एक RoutedEventArgs प्रदान करता है ताकि आप इसे बांध सकें।

अब एक्सएमएल दृश्य में

<Xceed:WatermarkPasswordBox Watermark="Input your Password" Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="7" PasswordChar="*" >

        <i:Interaction.Triggers>

            <i:EventTrigger EventName="PasswordChanged">

                <prism:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path= DataContext.PasswordChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path= Password}"/>

            </i:EventTrigger>

        </i:Interaction.Triggers>

    </Xceed:WatermarkPasswordBox>

या

<PasswordBox Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="7" PasswordChar="*" >

        <i:Interaction.Triggers>

            <i:EventTrigger EventName="PasswordChanged">

                <prism:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path= DataContext.PasswordChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path= Password}"/>

            </i:EventTrigger>

        </i:Interaction.Triggers>

    </PasswordBox>

0

भेजें SecureStringएक संलग्न व्यवहार का उपयोग करके दृश्य मॉडल को औरICommand

MVVM को लागू करते समय कोड-पीछे कुछ भी गलत नहीं है। MVVM एक वास्तुशिल्प पैटर्न है जिसका उद्देश्य मॉडल / व्यवसाय तर्क से दृश्य को अलग करना है। MVVM यह बताता है कि इस लक्ष्य को एक प्रतिलिपि प्रस्तुत करने योग्य तरीके (पैटर्न) में कैसे प्राप्त किया जाए। यह कार्यान्वयन विवरणों के बारे में परवाह नहीं करता है, जैसे कि आप संरचना को कैसे देखते हैं या कार्यान्वित करते हैं। यह केवल सीमाओं को खींचता है और परिभाषित करता है कि इस पैटर्न, शब्दावली के संदर्भ में क्या दृश्य, दृश्य मॉडल और क्या मॉडल है।

MVVM भाषा (XAML या C #) या कंपाइलर (partial क्लासेस) की । भाषा स्वतंत्र होना एक डिजाइन पैटर्न की अनिवार्य विशेषता है - यह भाषा तटस्थ होनी चाहिए।

हालाँकि, कोड-बैक में कुछ ड्रा बैक हैं जैसे कि आपके UI लॉजिक को समझना कठिन है, जब इसे XAML और C # के बीच बेतहाशा वितरित किया जाता है। लेकिन C # में सबसे महत्वपूर्ण UI लॉजिक या टेम्प्लेट, स्टाइल, ट्रिगर्स, एनिमेशन आदि को लागू करना XAML का उपयोग करने की तुलना में बहुत जटिल और बदसूरत / कम पठनीय है। XAML एक मार्कअप भाषा है जो ऑब्जेक्ट पदानुक्रम की कल्पना करने के लिए टैग और नेस्टिंग का उपयोग करती है। XAML का उपयोग करके UI बनाना बहुत सुविधाजनक है। यद्यपि ऐसी स्थितियां हैं जहां आप सी # (या कोड-पीछे) में यूआई तर्क को लागू करने के लिए ठीक चयन कर रहे हैं। संभालनाPasswordBox एक उदाहरण है।

इस कारण से PasswordBoxकोड-हैंडल में हैंडलिंग से निपटने के लिएPasswordBox.PasswordChanged , MVVM पैटर्न का कोई उल्लंघन नहीं है।

एक स्पष्ट उल्लंघन PasswordBoxदृश्य मॉडल के लिए एक नियंत्रण ( ) पारित करना होगा । कई समाधान इस तरह की सलाह देते हैं, जैसे कि उदाहरण के PasswordBoxरूप में खाड़ी गुजर रही हैICommand.CommandParameter कि दृश्य मॉडल । जाहिर है एक बहुत खराब और अनावश्यक सिफारिश।

यदि आप C # के उपयोग के बारे में परवाह नहीं करते हैं, लेकिन बस अपनी कोड-बैक फ़ाइल को साफ रखना चाहते हैं या बस एक व्यवहार / UI लॉजिक को एनकोड करना चाहते हैं, तो आप हमेशा संलग्न गुणों का उपयोग कर सकते हैं और संलग्न व्यवहार को लागू कर सकते हैं।

कुख्यात चौड़ी फैली हुई सहायक के विरोध में जो सादे पाठ पासवर्ड (वास्तव में खराब विरोधी पैटर्न और सुरक्षा जोखिम) के लिए बाध्य करने में सक्षम है, यह व्यवहार ICommandपासवर्ड को SecureStringदृश्य मॉडल के रूप में भेजने के लिए उपयोग करता है , जब भी घटना को PasswordBoxउठाता है PasswordBox.PasswordChanged

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <PasswordBox PasswordBox.Command="{Binding VerifyPasswordCommand}" />
</Window>

ViewModel.cs

public class ViewModel : INotifyPropertyChanged
{
  public ICommand VerifyPasswordCommand => new RelayCommand(VerifyPassword);

  public void VerifyPassword(object commadParameter)
  {
    if (commandParameter is SecureString secureString)
    {
      IntPtr valuePtr = IntPtr.Zero;
      try
      {
        valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
        string plainTextPassword = Marshal.PtrToStringUni(valuePtr);

        // Handle plain text password. 
        // It's recommended to convert the SecureString to plain text in the model, when really needed.
      } 
      finally 
      {
        Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
      }
    }
  }
}

PasswordBox.cs

// Attached behavior
class PasswordBox : DependencyObject
{
  #region Command attached property

  public static readonly DependencyProperty CommandProperty =
    DependencyProperty.RegisterAttached(
      "Command",
      typeof(ICommand),
      typeof(PasswordBox),
      new PropertyMetadata(default(ICommand), PasswordBox.OnSendPasswordCommandChanged));

  public static void SetCommand(DependencyObject attachingElement, ICommand value) =>
    attachingElement.SetValue(PasswordBox.CommandProperty, value);

  public static ICommand GetCommand(DependencyObject attachingElement) =>
    (ICommand) attachingElement.GetValue(PasswordBox.CommandProperty);

  #endregion

  private static void OnSendPasswordCommandChanged(
    DependencyObject attachingElement,
    DependencyPropertyChangedEventArgs e)
  {
    if (!(attachingElement is System.Windows.Controls.PasswordBox passwordBox))
    {
      throw new ArgumentException("Attaching element must be of type 'PasswordBox'");
    }

    if (e.OldValue != null)
    {
      return;
    }

    WeakEventManager<object, RoutedEventArgs>.AddHandler(
      passwordBox,
      nameof(System.Windows.Controls.PasswordBox.PasswordChanged),
      SendPassword_OnPasswordChanged);
  }

  private static void SendPassword_OnPasswordChanged(object sender, RoutedEventArgs e)
  {
    var attachedElement = sender as System.Windows.Controls.PasswordBox;
    SecureString commandParameter = attachedElement?.SecurePassword;
    if (commandParameter == null || commandParameter.Length < 1)
    {
      return;
    }

    ICommand sendCommand = GetCommand(attachedElement);
    sendCommand?.Execute(commandParameter);
  }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.