简体   繁体   中英

DataBinding Not Working Properly in WPF ListView To TextBlock

I'm making a program which holds a data of some cryptocurrencies. Howewer I have a problem. If I make data binding like this, it doesn't cause a problem:

            //Set binding path to Text property
        Binding binding = new Binding("Text");
        binding.Source = txt;
        binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
        binding.Mode = BindingMode.TwoWay;
        //Attach a binding to the listview which holds the data
        lst.SetBinding(ListView.SelectedItemProperty, binding);

But if I use below one instead of above one, the TextBlock isn't updating after each property change, it's updated after changing the selected item and selecting the same one:

            //Set binding path to SelectedItem property
        Binding binding = new Binding("SelectedItem");
        binding.Source = lst;
        binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
        binding.Mode = BindingMode.TwoWay;
        //Attach a binding to the TextBlock which shows the data below the list
        txt.SetBinding(TextBlock.TextProperty, binding);

This is the constructor of this project:

        public FilteringSample()
    {
        InitializeComponent();
        //Set binding path to Text property
        Binding binding = new Binding("Text");
        binding.Source = txt;
        binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
        binding.Mode = BindingMode.TwoWay;
        //Attach a binding to the listview which holds the data
        lst.SetBinding(ListView.SelectedItemProperty, binding);
        WebClient webClient = new WebClient();
        string data1 = webClient.DownloadString("https://www.coingecko.com/tr/coins/solana");
        string data2 = webClient.DownloadString("https://www.coingecko.com/tr/coins/bitcoin");
        HtmlDocument htmlDocument1 = new HtmlDocument();
        HtmlDocument htmlDocument2 = new HtmlDocument();
        htmlDocument1.LoadHtml(data1);
        string text1= htmlDocument1.DocumentNode.SelectSingleNode("/html/body/div[5]/div[5]/div[1]/div/div[1]/div[3]/div/div[1]/span[1]/span").InnerText;
        htmlDocument2.LoadHtml(data2);
        string text2 = htmlDocument2.DocumentNode.SelectSingleNode("/html/body/div[5]/div[5]/div[1]/div/div[1]/div[3]/div/div[1]/span[1]/span").InnerText;
        ObservableCollection<Currency> list = new ObservableCollection<Currency>();
        list.Add(new Currency(){Name="Solana", RealValue = text1 });
        list.Add(new Currency() { Name = "Bitcoin", RealValue = text2 });
        foreach(Currency cry in list)
        {
            lst.Items.Add(cry);
        }
    }

This is the Currency Class that has implemented the INotifyPropertyChanged interface:

    public class Currency:INotifyPropertyChanged
{
    public string Name { get; set; }
    public string Value { get; set; }

    public string RealValue
    {
        get { return Value; }
        set
        {
            if (true)
            {
                Value = value;
                NotifyPropertyChanged("RealValue");
            }
        }
    }
    public event PropertyChangedEventHandler? PropertyChanged;

    public override string? ToString()
    {
        return Name + " = " + Value;
    }
    public void NotifyPropertyChanged(string propName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
            
    }
}

I used a background worker to update the data every 100 ms, which is the code below:

        private void Window_ContentRendered(object sender, EventArgs e)
    {
        BackgroundWorker backgroundWorker=new BackgroundWorker();
        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.DoWork += BackgroundWorker_DoWork;
        backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
        backgroundWorker.RunWorkerAsync();
    }
    private void BackgroundWorker_ProgressChanged(object? sender, ProgressChangedEventArgs e)
    {
        List<string> list = e.UserState as List<string>;
        (lst.Items[0] as Currency).RealValue = list[0];
        (lst.Items[1] as Currency).RealValue = list[1];
        lst.Items.Refresh();
    }
    private void BackgroundWorker_DoWork(object? sender, DoWorkEventArgs e)
    {
        WebClient webClient = new WebClient();
        while (true)
        {
            string data1 = webClient.DownloadString("https://min-api.cryptocompare.com/data/price?fsym=sol&tsyms=USD");
            string data2 = webClient.DownloadString("https://min-api.cryptocompare.com/data/price?fsym=eth&tsyms=USD");
            HtmlDocument htmlDocument1 = new HtmlDocument();
            HtmlDocument htmlDocument2 = new HtmlDocument();
            htmlDocument1.LoadHtml(data1);
            List<string> list=new List<string>() {data1,data2};
            (sender as BackgroundWorker).ReportProgress(0,list);
            Thread.Sleep(100);
        }
    }

And the last thing, here is the XAML code:

<Window x:Class="WpfTutorialSamples.ListView_control.FilteringSample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="FilteringSample" Height="200" Width="300" Background="Transparent" ContentRendered="Window_ContentRendered"
    SizeChanged="Window_SizeChanged">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <ListView x:Name="lst" ScrollViewer.HorizontalScrollBarVisibility="Hidden" SelectionChanged="lst_SelectionChanged">
        <ListView.Resources>
            <Style TargetType="ListView">
                <Setter Property="Background">
                    <Setter.Value>
                        <LinearGradientBrush>
                            <GradientStop Color="HotPink" Offset="0"/>
                            <GradientStop Color="CadetBlue" Offset="0.5"/>
                            <GradientStop Color="BlanchedAlmond" Offset="1"/>
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListView.Resources>
        <ListView.View>
            <GridView AllowsColumnReorder="False" x:Name="grd">
                <GridView.ColumnHeaderContainerStyle>
                    <Style TargetType="{x:Type GridViewColumnHeader}">
                        <Setter Property="Foreground" Value="Purple"/>
                        <Setter Property="FontWeight" Value="ExtraBold"/>
                        <Setter Property="FontSize" Value="15"/>
                        <Setter Property="IsHitTestVisible" Value="False"/>
                    </Style>
                </GridView.ColumnHeaderContainerStyle>
                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="150"/>
                <GridViewColumn Header="Value" DisplayMemberBinding="{Binding Value}" Width="Auto"/>
            </GridView>
        </ListView.View>
    </ListView>
    <Grid Background="Aqua" Grid.Row="1">
        <TextBlock x:Name="txt" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" TextWrapping="Wrap"/>
    </Grid>
</Grid>
</Window>

So, what should I do to solve this problem?

If you just want your TextBlock "txt" to Display your Selected Item, the Binding should look like this:

<TextBlock x:Name="txt" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" TextWrapping="Wrap" Text="{Binding ElementName=lst, Path=SelectedItem, Mode=OneWay}"/>

Notice the "Mode=OneWay"? It prevents your TextBlock from setting the ListViews SelectedItem.

You also keep your code-behind free of unnecessary clutter this way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM