passing a string from one user control to a second user control via INotify wihtin MVVM

China☆狼群 提交于 2019-12-14 04:22:50

问题


I am having some issues with passing a string from one user control to a second user control via INotify.

Within the view I have a listbox which is bound to a ObservableCollection of type string titled SearchHistory whenever a user types a value into a textbox which is within a user control I pass the value typed into the collection and display it within the listbox control ( somewhat of a history of terms entered). This works fine.

I am now trying to select the listbox item and pass it back to the textbox within the control via INotifyPropertyChanged. While I see the value selected being passed back to the property within the view model it is not updating the textbox within the view with the new selected value.

Within my view I have the following xaml for the listbox item

<ListBox Margin="0,0,1,0"
                         Background="{x:Null}"
                         BorderBrush="{x:Null}"
                         BorderThickness="0"
                         ItemsSource="{Binding SearchHistory}" 
                         SelectionChanged="ListBox_SelectionChanged" />

Within the view codebehind I have the following ( I opted to place this in the code behind since the functionality was specific to the view in passing the string between two controls. I don't think this needs to be in the viewmodel as a command but if so please let me know otherwise)

    public partial class viewSearch : Page
{
    private SearchViewModel _ViewModel;

    #region Constructor
    public viewSearch()
    {
        InitializeComponent();
        CloseFilterPanel();
        CloseHistoryPanel();
        CloseSaveSearchPanel();
        this._ViewModel = new SearchViewModel();
        this.DataContext = this._ViewModel;
    }

... edited for brevity

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if(((ListBox)sender).SelectedItem != null)
        _ViewModel.SearchTerm = (((ListBox)sender).SelectedItem.ToString());
        this.NotifyPropertyChanged(_ViewModel.SearchTerm);
    }

Within the ViewModel I also have the public property SearchTerm declared

        public string SearchTerm
    {
        get
        {
            return this._SearchTerm;
        }
        set
        {
            if (this._SearchTerm == value)
                return;

            // Set the new value and notify
            this._SearchTerm = value;
            this.NotifyPropertyChanged("SearchTerm"); //this binds to UI search textbox
       }

As I stated when I step through this the value is passed from the view to the view model and I see the above property updated however when it hits the section that binds to the UI nothing happens in the UI.

Below is the partial (I excluded the grid wrapper and header context) xaml for the usercontrol that contains the textblock. No data context is set within the control as I inherit the data context from the parent view.

<TextBox x:Name="txtSearchField"
        Grid.Column="0"
        Margin="5,3,5,1.333"
        ap:AttachedProperties.InitialFocus="True"
        FontSize="16"
        KeyDown="txtSearchField_KeyDown"
        Padding="2,5,10,1"
        Style="{StaticResource SearchTxtBoxStyle}"
        Text="{Binding SearchTerm, Mode=TwoWay}"
        TextAlignment="Right"
        ToolTipService.ToolTip="{StaticResource TTsearchField}">
        <i:Interaction.Triggers>
            <ei:KeyTrigger FiredOn="KeyUp" Key="Enter">
                <i:InvokeCommandAction Command="{Binding GetSearchResultCommand, Mode=OneWay}"/>
            </ei:KeyTrigger>
        </i:Interaction.Triggers>           
    </TextBox>

And finally the control as it is placed in the main view

<srch:SearchControl x:Name="ucSearchControl" DataContext="{Binding}" Grid.Row="0" />

Any tips or suggestions are appreciated. Thank you in advance


回答1:


Working through this, I found the issue to be in attempting to pass a string back to the property as well as how I was originally storing my values in a generic collection. Below is how I resolved the issue.

We'll start first with the xaml within my search user control. This control contained the text field I wanted updated.

<TextBox x:Name="txtSearchField"
        Grid.Column="0"
        Margin="5,3,5,1.333"
        ap:AttachedProperties.InitialFocus="True"
        FontSize="16"
        KeyDown="txtSearchField_KeyDown"
        Padding="2,5,10,1"
        Style="{StaticResource SearchTxtBoxStyle}"
        Text="{Binding SearchTerm, Mode=TwoWay}"
        TextAlignment="Right"
        ToolTipService.ToolTip="{StaticResource TTsearchField}">
        <i:Interaction.Triggers>
            <ei:KeyTrigger FiredOn="KeyUp" Key="Enter">
                <i:InvokeCommandAction Command="{Binding GetSearchResultCommand, Mode=OneWay}"/>
            </ei:KeyTrigger>
        </i:Interaction.Triggers>           
    </TextBox>

Within the ViewModel I declared a new collection

delegate command

private ObservableCollection<SearchHistoryModel> _SearchHistory = new ObservableCollection<SearchHistoryModel>();

Created my property

public ObservableCollection<SearchHistoryModel> SearchHistory
    {

        get
        {
            return this._SearchHistory;
        }
        private set
        {
            if (this._SearchHistory == value)
                return;
            this._SearchHistory = value;
            this.NotifyPropertyChanged("SearchHistory");
        }
    }

And the command to the model

private void GetSearchResultCommandExecute(object parameter)
    {
        //query
       this.SearchResults = this._DataModel.GetSearchResults(this.SearchTerm);
        //search history items
      this.SearchHistory = this._DataModel.AddSearchHistoryItem(this.SearchTerm);
    }

From within the Model I then created a method to add the strings to my collection

delegate command

private ObservableCollection<SearchHistoryModel> SearchHistory = new ObservableCollection<SearchHistoryModel>();

Method

public ObservableCollection<SearchHistoryModel>AddSearchHistoryItem(string searchHistoryItem)
    {
        SearchHistoryModel shm = new SearchHistoryModel();
        shm.SearchHistoryString = searchHistoryItem.ToString();
        SearchHistory.Add(shm);
        return this.SearchHistory;
            //this.SearchHistory = new ObservableCollection<SearchHistoryModel>();
            //this.SearchHistory.Add(searchHistoryItem);                 
    }

Taking this route I was able to push the collection back to the UI and then bind this to Listbox

then within my View I was able to then use the selection changed event to update the Search History Observable Collection. While I am not 100% certain I think the issue was related to how the observable collection handles NotificationChanged events. If anyone has any additional insight to this I would appreciate it.

seach.xaml.cs code behind

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (((ListBox)sender).SelectedItem != null)
            _ViewModel.SearchTerm = (((ListBox)sender).SelectedItem as SearchHistoryModel).SearchHistoryString.ToString();
        this.NotifyPropertyChanged(_ViewModel.SearchTerm);
    }

*Note for clarity the SearchHistoryModely only contained one dumb property titled SearchHistoryString

 public class SearchHistoryModel
{
    public string SearchHistoryString { get; set; }
}

The main thing to note here is that I cast the Listbox Item to my collection / property. Since the items would never be visible unless they have an object within them I did not test for null values however I think I'll go back later and update that just for tidiness.

Making these changes now allow for me to pass items from the listbox collection back to the textBlock field ( or vice versa).

I would welcome any other alternative or more streamlined solutions. Thank you




回答2:


its not tested but when you just wanna set the selecteditem from your listbox to your textbox.text property, this would work: (i assume the Name of your TextBox is txtSearch)

 <ListBox Margin="0,0,1,0"
                     Background="{x:Null}"
                     BorderBrush="{x:Null}"
                     BorderThickness="0"
                     ItemsSource="{Binding SearchHistory}" 
                     SelectedItem="{Binding ElementName=txtSearch, Path=Text, Mode=OneWayToSource}" />



回答3:


If I understand correctly, there's a very simple way to achieve what you want to do, in an "MVVM" fashion.

You can have the SelectedItem in the ListBox "TwoWay" bound to a property in the first control's ViewModel. In the setter of that property, you can send a message, say SearchTermSelectedMessage, which would be received by the other control's ViewModel, and set a property accordingly, which would update the UI. For this, you can use MVVMLight's Messenger object.

Hope this helps.



来源:https://stackoverflow.com/questions/6115994/passing-a-string-from-one-user-control-to-a-second-user-control-via-inotify-wiht

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!