如果ObservableCollection,绑定列表到文本框不会更新

时间:2016-04-16 07:49:50

标签: c# wpf xaml

我在WPF中创建自定义控件。我将List<IMyInterface>绑定到dependency property。这又会再次绑定到ListBox,它会按预期显示所有项目。

我现在想要将此列表中的1个项目绑定到Textblock,因此我将整个列表绑定到textblock。我有一个converter来提取我想要的单个项目。

它工作正常,但由于一些原因,我想使用ObservableCollection代替List

奇怪的是,当我在运行时更改ObservabaleCollection中的值时,该值会显示在ListBox(成功)中,但不会显示在我的textblock中。 converter甚至没有被击中!

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        this.Errors = new ObservableCollection<IEventDetail>();
        this.Errors.CollectionChanged += Errors_CollectionChanged;
        var bw = new BackgroundWorker();
        bw.DoWork += ((o, e) =>
        {
            System.Threading.Thread.Sleep(1500);
            Dispatcher.Invoke(() =>
            {
                this.Errors.Add(new MyEvents("example of some detail", "Failed title"));
            });

            System.Threading.Thread.Sleep(2500);

            Dispatcher.Invoke(() =>
            {
                this.Errors.Add(new MyEvents("Another example", "Failed title 2"));
            });
        });
        bw.RunWorkerAsync();//background worker for testing/debugging only
    }

    private void Errors_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        OnPropertyChanged("Errors");
    }

    private ObservableCollection<IEventDetail> _errors;
    public ObservableCollection<IEventDetail> Errors
    {
        get
        {
            return this._errors;
        }
        set
        {
            this._errors = value;
            OnPropertyChanged("Errors");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged == null)
            return;

        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

而xaml只是

 <local:Notify Events="{Binding Errors}" DockPanel.Dock="Right"/>

正如您所看到的,我已尝试使用CollectionChanged事件然后强制点燃INotifyPropertyChanged,但它已在我的Converter中解雇了ListBox正在更新(所以我知道绑定没问题)

这是UserControls xaml

 <TextBlock Text="{Binding Path=Events, RelativeSource={RelativeSource AncestorLevel=1, AncestorType=UserControl}, Mode=Default, Converter={StaticResource MostRecentConverter}}" Grid.Row="0" />

    <ListBox ItemsSource="{Binding Path=Events, RelativeSource={RelativeSource AncestorLevel=1,AncestorType=UserControl}, Mode=Default}" Grid.Row="1">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding EventTitle}" Style="{StaticResource txtBckRed}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

我还需要做点什么吗?

1 个答案:

答案 0 :(得分:1)

正如评论 function initPlasma() { /* MATH FUNCTIONS ------------------------------ */ function MathUtil() {} MathUtil.getDistance = function ( a, b ) { return Math.abs( Math.sqrt(a*a + b*b) ); }; MathUtil.randRangeDecimel = function ( min, max ) { return Math.random() * ( max - min ) + min; }; /* GRID CELL CLASS ------------------------------ */ var Cell = function( x, y, w, h ) { this.x = x; this.y = y; this.w = w; this.h = h; } Cell.prototype.update = function( r, g, b ) { this.r = r; this.g = g; this.b = b; this.draw(); }; Cell.prototype.draw = function() { if( !plasma ) return; // get color, based on distance var ctrlPt1 = MathUtil.getDistance( this.x - plasma.controlPoints[0].x, this.y - plasma.controlPoints[0].y ); var ctrlPt2 = MathUtil.getDistance( this.x - plasma.controlPoints[1].x, this.y - plasma.controlPoints[1].y ); var ctrlPt3 = MathUtil.getDistance( this.x - plasma.controlPoints[2].x, this.y - plasma.controlPoints[2].y ); var rVal = .5+.5*Math.sin(this.r) * Math.cos(ctrlPt1/100) * Math.cos(ctrlPt2/100) * Math.sin(ctrlPt3/100); var gVal = .2+.5*Math.sin(this.g) * Math.sin(ctrlPt1/100) * Math.sin(ctrlPt2/100) * Math.sin(ctrlPt3/100); var bVal = .2+.5*Math.cos(this.b) * Math.sin(ctrlPt1/100) * Math.cos(ctrlPt2/100) * Math.sin(ctrlPt3/100) // draw pixel to canvas plasma.context.fillStyle = "rgb("+ Math.round( 127 + rVal * 255 ) +","+ Math.round( 127 + gVal * 255 ) +","+ Math.round( 127 + bVal * 255 ) +")"; plasma.context.fillRect ( this.x, this.y, this.w, this.h ); }; /* CONTROL POINT CLASS ------------------------------ */ var ControlPoint = function( canvasW, canvasH ) { // create random x,y starting point this.incX = MathUtil.randRangeDecimel( 0, 2 * Math.PI ); this.incY = MathUtil.randRangeDecimel( 0, 2 * Math.PI ); // create random x,y oscillating speed this.incXSpeed = MathUtil.randRangeDecimel( .01, .05 ); this.incYSpeed = MathUtil.randRangeDecimel( .01, .05 ); // store center point to oscillate around this.centerX = canvasW / 2; this.centerY = canvasH / 2; } ControlPoint.prototype.update = function() { // increment oscillating based on randomly-calculated speed this.incX += this.incXSpeed; this.incY += this.incYSpeed; // update coordinate this.x = this.centerX + this.centerX * Math.sin( this.incX ); this.y = this.centerY + this.centerY * Math.sin( this.incY ); }; /* PLASMA CLASS ------------------------------ */ var Plasma = function() { this.COLS = 50; this.ROWS = 50; this.CANVAS_W = 700; this.CANVAS_H = 700; this.FPS = 1000/30; this.NUM_CONTROL_POINTS = 3; this.startR = MathUtil.randRangeDecimel(0,2*Math.PI); this.startG = MathUtil.randRangeDecimel(0,2*Math.PI); this.startB = MathUtil.randRangeDecimel(0,2*Math.PI); this.startIncR = MathUtil.randRangeDecimel(.001,.05); this.startIncG = MathUtil.randRangeDecimel(.001,.05); this.startIncB = MathUtil.randRangeDecimel(.001,.05); this.incR = MathUtil.randRangeDecimel(.0001,.001); this.incG = MathUtil.randRangeDecimel(.0001,.001); this.incB = MathUtil.randRangeDecimel(.0001,.001); this.canvas; this.context; this.grid; this.buildStage(); this.createGrid(); this.createControlPoints(); this.addSaveFunctionality(); var self = this; setInterval( function(){ self.update(); }, this.FPS ); }; Plasma.prototype.buildStage = function() { // create and attach canvas element this.canvas = document.createElement('canvas'); this.canvas.width = this.CANVAS_W; this.canvas.height = this.CANVAS_H; document.body.appendChild( this.canvas ); // store graphical context this.context = this.canvas.getContext("2d"); }; Plasma.prototype.createGrid = function() { // calculate "pixel" size var boxW = this.CANVAS_W / this.COLS; var boxH = this.CANVAS_H / this.ROWS; // create 2D array of grid cells this.grid = new Array( this.COLS ); for( var i = 0; i < this.COLS; i++ ) { this.grid[ i ] = new Array( this.ROWS ) for( var j = 0; j < this.ROWS; j++ ) { this.grid[ i ][ j ] = new Cell( i * boxW, j * boxH, boxW, boxH ); } } }; Plasma.prototype.createControlPoints = function() { this.controlPoints = []; for ( var i = 0; i < this.NUM_CONTROL_POINTS; i++ ) { this.controlPoints.push( new ControlPoint( this.CANVAS_W, this.CANVAS_H ) ); } }; Plasma.prototype.addSaveFunctionality = function() { var self = this; this.canvas.addEventListener("click", function(e) { window.open( self.canvas.toDataURL("image/jpeg") ); }, false); }; Plasma.prototype.update = function() { // increment the starting colors this.startR += this.startIncR; var curR = this.startR; this.startG += this.startIncG; var curG = this.startG; this.startB += this.startIncB; var curB = this.startB; // update control points for ( var i = 0; i < this.NUM_CONTROL_POINTS; i++ ) { this.controlPoints[i].update(); } // increment grid cells and draw to canvas for (var i = 0; i < this.COLS; i++) { for (var j = 0; j < this.ROWS; j++) { // send new base color to cells this.grid[i][j].update( curR, curG, curB ); // increment color as we traverse the grid curR += this.incR; curG += this.incG * 3; curB += this.incB; } } }; // kick off the plasma controller var plasma = new Plasma(); } 中所提到的,仅限于TextBlock属性更改(不是其项目),因此除非创建新的集合实例,否则它不会触发。对Events做出反应是INotifyCollectionChanged属性的特征。

解决方案1 ​​

暂时保留所有内容,只需提供ItemsSource某个名称

即可
TextBlock

并订阅您<TextBlock Text="{Binding ...}" x:Name="myTextBlock"/> 内的CollectionChanged事件,您手动强制绑定目标进行更新

UserControl

解决方案2

使用自定义属性创建自己的继承自public partial class MyUserControl : UserControl { public static readonly DependencyProperty EventsProperty = DependencyProperty.Register("Events", typeof(IEnumerable), typeof(MyUserControl), new PropertyMetadata(EventsPropertyChanged)); private static void EventsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((MyUserControl)d).EventsPropertyChanged(e); } private void EventsPropertyChanged(DependencyPropertyChangedEventArgs args) { var newCollection = args.NewValue as INotifyCollectionChanged; if (newCollection != null) newCollection.CollectionChanged += (s, e) => myTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget(); } public IEnumerable Events { get { return (IEnumerable)GetValue(EventsProperty); } set { SetValue(EventsProperty, value); } } } 的集合类,该属性将执行转换器的操作

ObservableCollection<T>

并将public class MyObservableCollection<T> : ObservableCollection<T> { private string _convertedText; protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { base.OnCollectionChanged(e); this.ConvertedText = ...; // <- do here what your IValueConverter does } public string ConvertedText { get { return _convertedText; } private set { _convertedText = value; OnPropertyChanged(new PropertyChangedEventArgs("ConvertedText")); } } } 绑定到TextBlock.Text属性,而无需转换器