我在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>
我还需要做点什么吗?
答案 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
属性,而无需转换器